diff --git a/Utils/Package.swift b/Utils/Package.swift index cee7018d..d12a015a 100644 --- a/Utils/Package.swift +++ b/Utils/Package.swift @@ -60,7 +60,8 @@ let package = Package( dependencies: [ "Utils", .product(name: "Testing", package: "swift-testing"), - ] + ], + resources: [.copy("Resources")] ), ], swiftLanguageVersions: [.version("6")] diff --git a/Utils/Sources/erasure-coding/bindings.h b/Utils/Sources/erasure-coding/bindings.h index b7824226..27207372 100644 --- a/Utils/Sources/erasure-coding/bindings.h +++ b/Utils/Sources/erasure-coding/bindings.h @@ -8,6 +8,8 @@ #define POINT_SIZE 2 #define SUBSHARD_POINTS 6 +typedef uint16_t ChunkIndex; + #define MAX_CHUNKS 16384 @@ -38,31 +40,39 @@ #define SUBSHARD_SIZE (POINT_SIZE * SUBSHARD_POINTS) /** - * The index of an erasure chunk. + * Subshard uses some temp memory, so these should be used multiple time instead of allocating. */ -typedef struct ChunkIndex ChunkIndex; +typedef struct SubShardDecoder SubShardDecoder; /** - * Result of the reconstruct. + * Subshard uses some temp memory, so these should be used multiple time instead of allocating. */ -typedef struct ReconstructResult ReconstructResult; +typedef struct SubShardEncoder SubShardEncoder; -/** - * Fix size segment of a larger data. - * Data is padded when unaligned with - * the segment size. - */ -typedef struct Segment Segment; +typedef struct CSegment { + uint8_t *data; + uint32_t index; +} CSegment; -/** - * Subshard uses some temp memory, so these should be used multiple time instead of allocating. - */ -typedef struct SubShardDecoder SubShardDecoder; +typedef struct SegmentTuple { + uint8_t index; + struct CSegment segment; +} SegmentTuple; /** - * Subshard uses some temp memory, so these should be used multiple time instead of allocating. + * Result of the reconstruct. */ -typedef struct SubShardEncoder SubShardEncoder; +typedef struct ReconstructResult { + struct SegmentTuple *segments; + uintptr_t num_segments; + uintptr_t num_decodes; +} ReconstructResult; + +typedef struct SubShardTuple { + uint8_t seg_index; + ChunkIndex chunk_index; + uint8_t shard[12]; +} SubShardTuple; /** * Subshard (points in sequential orders). @@ -86,7 +96,7 @@ void subshard_encoder_free(struct SubShardEncoder *encoder); * out_len is N * TOTAL_SHARDS */ void subshard_encoder_construct(struct SubShardEncoder *encoder, - const struct Segment *segments, + const struct CSegment *segments, uintptr_t num_segments, bool *success, uint8_t (***out_chunks)[12], @@ -102,6 +112,10 @@ struct SubShardDecoder *subshard_decoder_new(void); */ void subshard_decoder_free(struct SubShardDecoder *decoder); -uintptr_t reconstruct_result_get_num_decodes(const struct ReconstructResult *result); - -void reconstruct_result_free(struct ReconstructResult *result); +/** + * Reconstructs data from a list of subshards. + */ +struct ReconstructResult *subshard_decoder_reconstruct(struct SubShardDecoder *decoder, + const struct SubShardTuple *subshards, + uintptr_t num_subshards, + bool *success); diff --git a/Utils/Sources/erasure-coding/cbindgen.toml b/Utils/Sources/erasure-coding/cbindgen.toml index 65c6ce2f..6f3e3578 100644 --- a/Utils/Sources/erasure-coding/cbindgen.toml +++ b/Utils/Sources/erasure-coding/cbindgen.toml @@ -5,6 +5,8 @@ after_includes = """ #define POINT_SIZE 2 #define SUBSHARD_POINTS 6 +typedef uint16_t ChunkIndex; + """ autogen_warning = "/* Warning, this file is auto generated by cbindgen. Don't modify this manually. */" @@ -16,4 +18,5 @@ include = ["erasure-coding"] extra_bindings = ["erasure-coding"] [export] -include = ["Segment", "SubShard", "POINT_SIZE", "SUBSHARD_SIZE", "ChunkIndex"] +include = ["SubShard", "POINT_SIZE", "SUBSHARD_SIZE"] +exclude = ["ChunkIndex"] diff --git a/Utils/Sources/erasure-coding/src/ffi.rs b/Utils/Sources/erasure-coding/src/ffi.rs index 2be00cc5..5faf915d 100644 --- a/Utils/Sources/erasure-coding/src/ffi.rs +++ b/Utils/Sources/erasure-coding/src/ffi.rs @@ -1,6 +1,42 @@ -use erasure_coding::{ChunkIndex, Segment, SubShardDecoder, SubShardEncoder, TOTAL_SHARDS}; +use erasure_coding::{ + ChunkIndex, Segment, SubShardDecoder, SubShardEncoder, SEGMENT_SIZE, TOTAL_SHARDS, +}; use std::slice; +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct CSegment { + data: *mut u8, // Pointer to the data, length is SEGMENT_SIZE + index: u32, +} + +impl From for CSegment { + fn from(segment: Segment) -> Self { + let mut vec_data = Vec::from(*segment.data); + + let c_segment = CSegment { + data: vec_data.as_mut_ptr(), + index: segment.index, + }; + + // prevent Rust from freeing the Vec while CSegment is in use + std::mem::forget(vec_data); + + c_segment + } +} + +impl From for Segment { + fn from(c_segment: CSegment) -> Self { + let vec_data = unsafe { Vec::from_raw_parts(c_segment.data, SEGMENT_SIZE, SEGMENT_SIZE) }; + + Segment { + data: Box::new(vec_data.try_into().unwrap()), + index: c_segment.index, + } + } +} + /// Initializes a new SubShardEncoder. #[no_mangle] pub extern "C" fn subshard_encoder_new() -> *mut SubShardEncoder { @@ -22,7 +58,7 @@ pub extern "C" fn subshard_encoder_free(encoder: *mut SubShardEncoder) { #[no_mangle] pub extern "C" fn subshard_encoder_construct( encoder: *mut SubShardEncoder, - segments: *const Segment, + segments: *const CSegment, num_segments: usize, success: *mut bool, out_chunks: *mut *mut *mut [u8; 12], @@ -34,9 +70,15 @@ pub extern "C" fn subshard_encoder_construct( } let encoder = unsafe { &mut *encoder }; - let segments = unsafe { slice::from_raw_parts(segments, num_segments) }; - - match encoder.construct_chunks(segments) { + let segments_vec: Vec = unsafe { + slice::from_raw_parts(segments, num_segments) + .iter() + .map(|segment| Segment::from(*segment)) + .collect() + }; + let r_segments: &[Segment] = &segments_vec; + + match encoder.construct_chunks(r_segments) { Ok(result) => { let total_chunks = result.len() * TOTAL_SHARDS; let mut chunk_ptrs: Vec<*mut [u8; 12]> = Vec::with_capacity(total_chunks); @@ -77,47 +119,69 @@ pub extern "C" fn subshard_decoder_free(decoder: *mut SubShardDecoder) { } } +#[repr(C)] +pub struct SegmentTuple { + pub index: u8, + pub segment: CSegment, +} + /// Result of the reconstruct. #[repr(C)] pub struct ReconstructResult { - pub segments: *mut (u8, Segment), + pub segments: *mut SegmentTuple, pub num_segments: usize, pub num_decodes: usize, } -#[no_mangle] -pub extern "C" fn reconstruct_result_get_segments( - result: *const ReconstructResult, -) -> *const (u8, Segment) { - if result.is_null() { - return std::ptr::null(); - } - unsafe { (*result).segments } -} +// #[no_mangle] +// pub extern "C" fn reconstruct_result_get_segments( +// result: *const ReconstructResult, +// ) -> *const SegmentTuple { +// if result.is_null() { +// return std::ptr::null(); +// } +// unsafe { (*result).segments } +// } + +// #[no_mangle] +// pub extern "C" fn reconstruct_result_get_num_segments(result: *const ReconstructResult) -> usize { +// if result.is_null() { +// return 0; +// } +// unsafe { (*result).num_segments } +// } + +// #[no_mangle] +// pub extern "C" fn reconstruct_result_get_num_decodes(result: *const ReconstructResult) -> usize { +// if result.is_null() { +// return 0; +// } +// unsafe { (*result).num_decodes } +// } + +// #[no_mangle] +// pub extern "C" fn reconstruct_result_free(result: *mut ReconstructResult) { +// if !result.is_null() { +// unsafe { +// let boxed_result = Box::from_raw(result); +// drop(Box::from_raw(boxed_result.segments)); +// drop(boxed_result); +// } +// } +// } -#[no_mangle] -pub extern "C" fn reconstruct_result_get_num_decodes(result: *const ReconstructResult) -> usize { - if result.is_null() { - return 0; - } - unsafe { (*result).num_decodes } -} - -#[no_mangle] -pub extern "C" fn reconstruct_result_free(result: *mut ReconstructResult) { - if !result.is_null() { - unsafe { - let boxed_result = Box::from_raw(result); - drop(Box::from_raw(boxed_result.segments)); - } - } +#[repr(C)] +pub struct SubShardTuple { + pub seg_index: u8, + pub chunk_index: ChunkIndex, + pub shard: [u8; 12], } /// Reconstructs data from a list of subshards. #[no_mangle] pub extern "C" fn subshard_decoder_reconstruct( decoder: *mut SubShardDecoder, - subshards: *const (u8, ChunkIndex, [u8; 12]), + subshards: *const SubShardTuple, num_subshards: usize, success: *mut bool, ) -> *mut ReconstructResult { @@ -131,12 +195,18 @@ pub extern "C" fn subshard_decoder_reconstruct( let cloned_subshards: Vec<(u8, ChunkIndex, &[u8; 12])> = subshards_slice .iter() - .map(|&(a, b, ref c)| (a, b, c)) + .map(|t| (t.seg_index, t.chunk_index, &t.shard)) .collect(); match decoder.reconstruct(&mut cloned_subshards.iter().cloned()) { Ok((segments, num_decodes)) => { - let mut segments_vec: Vec<(u8, Segment)> = segments.into_iter().collect(); + let mut segments_vec: Vec = segments + .into_iter() + .map(|(index, segment)| SegmentTuple { + index, + segment: segment.into(), + }) + .collect(); let segments_len = segments_vec.len(); let segments_ptr = segments_vec.as_mut_ptr(); diff --git a/Utils/Tests/UtilsTests/Resources/erasure-coding-test-data.json b/Utils/Tests/UtilsTests/Resources/erasure-coding-test-data.json new file mode 100644 index 00000000..0326e951 --- /dev/null +++ b/Utils/Tests/UtilsTests/Resources/erasure-coding-test-data.json @@ -0,0 +1,1034 @@ +{ + "data": "9c077029c120e6de118edc7bfdd79ed4834115dbfa9ed309aa2339053bb3a07a313e536e8538c962b571fa57c15572ae44fb0f766b16b8e6a7c44ab69caa955c9ab37c217d963dd9d8e4b708348c899a8766a899434dfeacf18fe8d947477e0bc954537d9ad80cd9241205930531432c5c623a9c77ae0865c62b057ab1553331811fe0561f49fc69a6453b799914a0974e114f4e40775af050e723c7d20d0ba6f1ad6524b7a11ac3680da41d1c580cde055a2a1d70fabeaebe14699038b2db6ac4a0d362eeccd36bce908b62c8decfa9ef57f7514e25119288f81581bc15da084f1d977c0e7c3db6c976f6b2a11eb370dcaf4c4fc9fbd884eab541b3970144013ecaa56ae56580cdba4c87aac4e9aec4e6b293cac13ea48ba10c3bdc7408834a16b9f4f888aef5820e8f96e41bfd7e872f33e2773d43ebef7d3c45dc4d869fc6437012b01d14093dd21c18694896dbf295199f76a3a2ca2c3aa4fef98b3e35559bfc43924645bf9c5fd532a8d8466ab0f47c1ecfade8d90cdb3632d935ca96e4c97e51afb2b2dd92745c4b17e9e5efb3bbd1b9819faa980e18b4d5d0e2660d95d493cefeb158f8611ebde40b17df55981ea1fb24d9da7ea6cd2ee31583f1190df11547e7fe5159e0d25b4c1442baa331002f376e90548691b90c25f9a805d3391586e599d1d18aa0e12a0a2436bfcf4d936129bf6d083ec11a9d91419071aa9685e1cd99dbb9626d4031da55b5c6ed4f45722a06f9568d7a516fee5524bc4f86869f0f35cf5a2f499480aa62efa02ee377c0d286addce565e3c3e51d149a5818a59f94a14cb762b5e1882049c4ceb9ef38923b5702e871c6bfa9c7fe6d1c1cc2439078b5d816c6914f15d41cd60df4ba9dfa0b8ce68978fe681731c2c6a8caeff417137a4b1c59142060e29f59665c1550f6ee292c395bc08eb144a5d2383893a4cb3abf34f38e8396e8a574", + "segment": { + "segments": [ + { + "segment_ec": [ + "9c07", + "7029", + "c120", + "e6de", + "118e", + "dc7b", + "fdd7", + "9ed4", + "8341", + "15db", + "fa9e", + "d309", + "aa23", + "3905", + "3bb3", + "a07a", + "313e", + "536e", + "8538", + "c962", + "b571", + "fa57", + "c155", + "72ae", + "44fb", + "0f76", + "6b16", + "b8e6", + "a7c4", + "4ab6", + "9caa", + "955c", + "9ab3", + "7c21", + "7d96", + "3dd9", + "d8e4", + "b708", + "348c", + "899a", + "8766", + "a899", + "434d", + "feac", + "f18f", + "e8d9", + "4747", + "7e0b", + "c954", + "537d", + "9ad8", + "0cd9", + "2412", + "0593", + "0531", + "432c", + "5c62", + "3a9c", + "77ae", + "0865", + "c62b", + "057a", + "b155", + "3331", + "811f", + "e056", + "1f49", + "fc69", + "a645", + "3b79", + "9914", + "a097", + "4e11", + "4f4e", + "4077", + "5af0", + "50e7", + "23c7", + "d20d", + "0ba6", + "f1ad", + "6524", + "b7a1", + "1ac3", + "680d", + "a41d", + "1c58", + "0cde", + "055a", + "2a1d", + "70fa", + "beae", + "be14", + "6990", + "38b2", + "db6a", + "c4a0", + "d362", + "eecc", + "d36b", + "ce90", + "8b62", + "c8de", + "cfa9", + "ef57", + "f751", + "4e25", + "1192", + "88f8", + "1581", + "bc15", + "da08", + "4f1d", + "977c", + "0e7c", + "3db6", + "c976", + "f6b2", + "a11e", + "b370", + "dcaf", + "4c4f", + "c9fb", + "d884", + "eab5", + "41b3", + "9701", + "4401", + "3eca", + "a56a", + "e565", + "80cd", + "ba4c", + "87aa", + "c4e9", + "aec4", + "e6b2", + "93ca", + "c13e", + "a48b", + "a10c", + "3bdc", + "7408", + "834a", + "16b9", + "f4f8", + "88ae", + "f582", + "0e8f", + "96e4", + "1bfd", + "7e87", + "2f33", + "e277", + "3d43", + "ebef", + "7d3c", + "45dc", + "4d86", + "9fc6", + "4370", + "12b0", + "1d14", + "093d", + "d21c", + "1869", + "4896", + "dbf2", + "9519", + "9f76", + "a3a2", + "ca2c", + "3aa4", + "fef9", + "8b3e", + "3555", + "9bfc", + "4392", + "4645", + "bf9c", + "5fd5", + "32a8", + "d846", + "6ab0", + "f47c", + "1ecf", + "ade8", + "d90c", + "db36", + "32d9", + "35ca", + "96e4", + "c97e", + "51af", + "b2b2", + "dd92", + "745c", + "4b17", + "e9e5", + "efb3", + "bbd1", + "b981", + "9faa", + "980e", + "18b4", + "d5d0", + "e266", + "0d95", + "d493", + "cefe", + "b158", + "f861", + "1ebd", + "e40b", + "17df", + "5598", + "1ea1", + "fb24", + "d9da", + "7ea6", + "cd2e", + "e315", + "83f1", + "190d", + "f115", + "47e7", + "fe51", + "59e0", + "d25b", + "4c14", + "42ba", + "a331", + "002f", + "376e", + "9054", + "8691", + "b90c", + "25f9", + "a805", + "d339", + "1586", + "e599", + "d1d1", + "8aa0", + "e12a", + "0a24", + "36bf", + "cf4d", + "9361", + "29bf", + "6d08", + "3ec1", + "1a9d", + "9141", + "9071", + "aa96", + "85e1", + "cd99", + "dbb9", + "626d", + "4031", + "da55", + "b5c6", + "ed4f", + "4572", + "2a06", + "f956", + "8d7a", + "516f", + "ee55", + "24bc", + "4f86", + "869f", + "0f35", + "cf5a", + "2f49", + "9480", + "aa62", + "efa0", + "2ee3", + "77c0", + "d286", + "addc", + "e565", + "e3c3", + "e51d", + "149a", + "5818", + "a59f", + "94a1", + "4cb7", + "62b5", + "e188", + "2049", + "c4ce", + "b9ef", + "3892", + "3b57", + "02e8", + "71c6", + "bfa9", + "c7fe", + "6d1c", + "1cc2", + "4390", + "78b5", + "d816", + "c691", + "4f15", + "d41c", + "d60d", + "f4ba", + "9dfa", + "0b8c", + "e689", + "78fe", + "6817", + "31c2", + "c6a8", + "caef", + "f417", + "137a", + "4b1c", + "5914", + "2060", + "e29f", + "5966", + "5c15", + "50f6", + "ee29", + "2c39", + "5bc0", + "8eb1", + "44a5", + "d238", + "3893", + "a4cb", + "3abf", + "34f3", + "8e83", + "96e8", + "a574", + "7503", + "4f97", + "d16a", + "6727", + "081e", + "dac0", + "a596", + "e343", + "8a51", + "3ced", + "5f5e", + "f369", + "1b86", + "3608", + "eb77", + "460d", + "7830", + "6a6e", + "5ccb", + "1cdc", + "8db3", + "6dec", + "e52c", + "d3ee", + "44a3", + "d341", + "151a", + "9c0d", + "bb11", + "8ea2", + "70f6", + "2986", + "f97e", + "ce77", + "f718", + "7e1a", + "7257", + "af77", + "5938", + "c785", + "63af", + "6901", + "cb38", + "3897", + "3900", + "7561", + "66c8", + "9aa6", + "9121", + "1e58", + "c0cf", + "f447", + "61d9", + "26d0", + "1fb6", + "b3a4", + "aa9f", + "78f1", + "445a", + "14cb", + "b093", + "e605", + "cfd6", + "b2a5", + "2aa1", + "ae23", + "5ba2", + "265f", + "2288", + "3b34", + "92d2", + "6a62", + "32e4", + "dfdd", + "468d", + "d175", + "f4e5", + "8af3", + "e3eb", + "d46f", + "1930", + "2b3c", + "d61e", + "ae3e", + "67da", + "788b", + "c2cf", + "522f", + "62cf", + "b06b", + "3ceb", + "dd61", + "dfe3", + "7b0c", + "64d5", + "bcde", + "5542", + "d2e9", + "3f1b", + "bb17", + "1811", + "5799", + "fa3e", + "314b", + "dc37", + "798a", + "bc44", + "0894", + "1416", + "b9f9", + "65ab", + "b925", + "770f", + "991c", + "f147", + "f325", + "d285", + "0f3a", + "a781", + "9fa1", + "c75b", + "3231", + "2c47", + "7336", + "9d90", + "8d19", + "a848", + "192f", + "58b5", + "1493", + "3131", + "b8be", + "3d6f", + "0ddf", + "5d3b", + "09f8", + "9dd0", + "68fe", + "c354", + "c9c3", + "cd66", + "084c", + "38cb", + "378c", + "38d4", + "c0e4", + "9219", + "fd66", + "c2dc", + "0ae4", + "6bcb", + "29f4", + "b4d6", + "5fe8", + "e13e", + "7197", + "1ab1", + "bdfb", + "598c", + "a918", + "24eb", + "b38e", + "ae22", + "1684", + "3c33", + "a88b", + "58f9", + "e4db", + "117e", + "9ced", + "4b22", + "4cdc", + "02d6", + "130e", + "99be", + "7023", + "e7bc", + "2a72", + "763d", + "d84f", + "08f1", + "ca89", + "141a", + "41f2", + "0c5a", + "58ff", + "3a1f", + "26ee", + "63a2", + "4f01", + "7305", + "9877", + "ca93", + "d982", + "50af", + "0a4b", + "9d77", + "5574", + "96b2", + "bdf4", + "aafd", + "2732", + "dad1", + "90c8", + "ac95", + "8106", + "2d19", + "0125", + "a7db", + "0038", + "f270", + "70ba", + "a8e5", + "fad4", + "f3e5", + "bb3f", + "c4d5", + "1524", + "0d21", + "918c", + "5c95", + "2cf9", + "8825", + "3975", + "9de5", + "8062", + "a72b", + "7e68", + "fd10", + "1cb7", + "2570", + "913f", + "939e", + "5596", + "8448", + "7e50", + "5a7a", + "0d0a", + "48fd", + "4214", + "c819", + "8339", + "c2ca", + "9c64", + "12fa", + "bf75", + "97f4", + "7725", + "1743", + "c829", + "76ea", + "78a4", + "bde9", + "3058", + "1a2a", + "3cdc", + "1cea", + "8f88", + "b6f8", + "6890", + "454e", + "4f1a", + "bca3", + "db77", + "c624", + "0cf5", + "d324", + "5117", + "a4de", + "ce4e", + "6b89", + "1a76", + "bd50", + "5f19", + "73a8", + "799e", + "0f85", + "21ef", + "906c", + "5b27", + "0d58", + "58e9", + "67f1", + "7bae", + "86d5", + "6ac1", + "23e9", + "349a", + "c621", + "bace", + "bc1d", + "0434", + "3b11", + "6e11", + "d084", + "9ce7", + "5f5d", + "2338", + "0cb1", + "6fe3", + "3881", + "97fd", + "17a6", + "1dc5", + "29ef", + "8343", + "061e", + "9970", + "e7a6", + "f53d", + "c9ac", + "5a2a", + "1c15", + "53f1", + "87ee", + "3e6a", + "0cfa", + "b218", + "3da7", + "b6af", + "d501", + "c6b3", + "15cb", + "335a", + "1fb0", + "808a", + "37a9", + "8b79", + "ddfa", + "b0cf", + "9241", + "30ca", + "f632", + "8cd2", + "70a5", + "bd71", + "4c18", + "ffd3", + "759c", + "97d8", + "8baa", + "e377", + "0351", + "048b", + "3429", + "6784", + "969d", + "807c", + "6c9a", + "491a", + "1863", + "806e", + "3953", + "45bb", + "ee31", + "e37e", + "5150", + "0063", + "34ad", + "278a", + "7413", + "7e62", + "5975", + "28df", + "e54a", + "ec0b", + "6222", + "0986", + "3bef", + "33dd", + "3359", + "2a48", + "f7e1", + "2721", + "046b", + "51a0", + "2609", + "ac0a", + "6d80", + "11c1", + "c0eb", + "cae5", + "d5ad", + "7daf", + "6c00", + "4253", + "6908", + "32f8", + "0395", + "e826", + "d0c8", + "ac15", + "d7e9", + "15b7", + "ba72", + "a554", + "3d0b", + "dcd5", + "3de4", + "d58c", + "b0ea", + "4b7b", + "85ad", + "af07", + "fb79", + "4674", + "30aa", + "17fd", + "06b1", + "ecdc", + "1ea3", + "d0a1", + "1656", + "4b64", + "44d5", + "f819", + "8797", + "2299", + "8878", + "422e", + "210d", + "17c9", + "87f4", + "ec96", + "1054", + "011a", + "02da", + "5f62", + "a8b6", + "72fd", + "772d", + "aaf1", + "df81", + "aa49", + "d501", + "d417", + "0b87", + "eedb", + "fd53", + "89be", + "4a53", + "e796", + "3a90", + "450f", + "7d49", + "8243", + "237f", + "1bb8", + "e401", + "6d23", + "54a8", + "8141", + "eb56", + "206a", + "9a81", + "e3e6", + "f93c", + "76dc", + "d5e5", + "110c", + "ccf8", + "9a07", + "90e7", + "9285", + "2277", + "6091", + "c8e5", + "eb35", + "11d5", + "fc80", + "79fc", + "474e", + "8001", + "45b7", + "a40b", + "a375", + "b106", + "1cab", + "96b9", + "560e", + "448f", + "9224", + "7a34", + "3dfc", + "3c4b", + "39ff", + "8f47", + "b294", + "043c", + "8349", + "a58d", + "e46e", + "16b0", + "ec1d", + "2f95", + "8563", + "20da", + "b2d5", + "b6ca", + "f7ae", + "f0a7", + "aa76", + "f232", + "8f52", + "cd50", + "fd99", + "d8cd", + "9356", + "9337", + "548a", + "62aa", + "87ca", + "a9d4", + "d202", + "1110", + "3ebc", + "1162", + "d74a", + "ffcf", + "61d2", + "4839", + "f944", + "c697", + "d926", + "c60c", + "a633", + "91c6", + "3cc4", + "6c93", + "0d75", + "a1cc", + "1888", + "b5e0", + "27cc", + "1d8c", + "72c7", + "1e7e", + "a2a3", + "a3fd", + "0cd5", + "2c9e", + "3030", + "ee0e", + "27aa", + "ce4c", + "9409", + "2f3d", + "a83e", + "e72d", + "5a77", + "6dc9", + "d2ee", + "7ac9", + "98fe", + "3218", + "a64f", + "b938", + "232e", + "2859", + "b764", + "3cfc", + "f8f4", + "6fa4", + "1be4", + "4690", + "aefe", + "1fcf", + "25d9", + "b8f4", + "72fb", + "8e84", + "2f64", + "251e", + "6b56", + "d3d8", + "de04", + "da78", + "5cc3", + "ab02", + "7b03", + "50c1", + "dad7", + "d82b", + "f573", + "6407", + "ee3b", + "04cd", + "4a21", + "3808", + "cd00", + "1912", + "81ed", + "2f77", + "0520", + "9edd", + "2861", + "ef1c", + "2615", + "c842", + "064a", + "6fc9", + "e34e", + "ec3f", + "9c10", + "5f5c", + "e4e7", + "9422", + "c217", + "bd4e", + "71ad", + "d01b", + "2f47", + "ee70", + "726e", + "1342", + "faf4", + "01a3", + "b7d1", + "3f31", + "b84d", + "2dc5", + "3c2b", + "09a2", + "1703", + "7ed8", + "912f", + "94b5", + "5635", + "c3d6", + "90f1", + "96b7", + "250e", + "a217", + "014a", + "0563", + "6268", + "dcd4", + "c47a", + "d657", + "c058", + "7f91", + "60d6", + "3ab3", + "7ca5", + "1d25", + "eb9e", + "dff9", + "0bf9", + "fcfc", + "b826", + "72c5", + "5692", + "7a9f", + "f105", + "4847", + "b8d7", + "43f8", + "62d9", + "7d25", + "de6f", + "15d2", + "7e3c", + "acb9", + "9d73", + "7df9", + "234f", + "467e", + "8976", + "2745", + "d661", + "5600", + "7aff", + "66e6", + "43d1", + "664a", + "d084", + "0ae5", + "7293", + "d8b7", + "f117", + "bb0f" + ] + } + ] + } +}