| 1277 | } |
| 1278 | |
| 1279 | Status WriteDictionaries(const RecordBatch& batch) { |
| 1280 | ARROW_ASSIGN_OR_RAISE(const auto dictionaries, CollectDictionaries(batch, mapper_)); |
| 1281 | const auto equal_options = EqualOptions().nans_equal(true); |
| 1282 | |
| 1283 | for (const auto& pair : dictionaries) { |
| 1284 | int64_t dictionary_id = pair.first; |
| 1285 | const auto& dictionary = pair.second; |
| 1286 | |
| 1287 | // If a dictionary with this id was already emitted, check if it was the same. |
| 1288 | auto* last_dictionary = &last_dictionaries_[dictionary_id]; |
| 1289 | const bool dictionary_exists = (*last_dictionary != nullptr); |
| 1290 | int64_t delta_start = 0; |
| 1291 | if (dictionary_exists) { |
| 1292 | if ((*last_dictionary)->data() == dictionary->data()) { |
| 1293 | // Fast shortcut for a common case. |
| 1294 | // Same dictionary data by pointer => no need to emit it again |
| 1295 | continue; |
| 1296 | } |
| 1297 | const int64_t last_length = (*last_dictionary)->length(); |
| 1298 | const int64_t new_length = dictionary->length(); |
| 1299 | if (new_length == last_length && |
| 1300 | ((*last_dictionary)->Equals(dictionary, equal_options))) { |
| 1301 | // Same dictionary by value => no need to emit it again |
| 1302 | // (while this can have a CPU cost, this code path is required |
| 1303 | // for the IPC file format) |
| 1304 | continue; |
| 1305 | } |
| 1306 | |
| 1307 | // (the read path doesn't support outer dictionary deltas, don't emit them) |
| 1308 | if (new_length > last_length && options_.emit_dictionary_deltas && |
| 1309 | !HasNestedDict(*dictionary->data()) && |
| 1310 | ((*last_dictionary) |
| 1311 | ->RangeEquals(dictionary, 0, last_length, 0, equal_options))) { |
| 1312 | // New dictionary starts with the current dictionary |
| 1313 | delta_start = last_length; |
| 1314 | } |
| 1315 | |
| 1316 | if (is_file_format_ && !delta_start) { |
| 1317 | return Status::Invalid( |
| 1318 | "Dictionary replacement detected when writing IPC file format. " |
| 1319 | "Arrow IPC files only support a single non-delta dictionary for " |
| 1320 | "a given field across all batches."); |
| 1321 | } |
| 1322 | } |
| 1323 | |
| 1324 | IpcPayload payload; |
| 1325 | if (delta_start) { |
| 1326 | RETURN_NOT_OK(GetDictionaryPayload(dictionary_id, /*is_delta=*/true, |
| 1327 | dictionary->Slice(delta_start), options_, |
| 1328 | &payload)); |
| 1329 | } else { |
| 1330 | RETURN_NOT_OK( |
| 1331 | GetDictionaryPayload(dictionary_id, dictionary, options_, &payload)); |
| 1332 | } |
| 1333 | RETURN_NOT_OK(WritePayload(payload)); |
| 1334 | ++stats_.num_dictionary_batches; |
| 1335 | if (dictionary_exists) { |
| 1336 | if (delta_start) { |
no test coverage detected