namespace Invercargill.DataStructures { public class BinaryData : Enumerable, Promotion, Equatable>, Hashable { public enum Endianness { Native, BigEndian, LittleEndian, } public Endianness endianness { get; set; } private Enumerable> chunks; public override int? peek_count() { return null; } public override EnumerableInfo get_info() { return new EnumerableInfo.infer(this, EnumerableCategory.COMPUTED, chunks); } public override Tracker get_tracker () { return chunks.select_many(s => s).get_tracker(); } public BinaryData() { chunks = new Series>(); endianness = Endianness.Native; } public BinaryData.from_enumerable(Enumerable data) { chunks = Invercargill.single(data.to_buffer()); endianness = Endianness.Native; } public BinaryData.from_bytes(Bytes data) { chunks = Invercargill.single(Wrap.array(data.get_data())); endianness = Endianness.Native; } public BinaryData.from_byte_array(uint8[] data) { chunks = Invercargill.single(Wrap.array(data)); endianness = Endianness.Native; } public BinaryData.from_base64(string data) { chunks = Invercargill.single(Wrap.array(Base64.decode(data))); endianness = Endianness.Native; } public BinaryData.from_hex(string data) { var groomed = data.replace(" ", "").replace("-", "").replace(":", "").replace("0x", ""); var buffer = new Buffer(groomed.length/2); for(var i = 0; i < groomed.length/2; i++) { non_error(() => buffer[i] = (uint8)uint.parse(groomed[i*2:(i+1)*2], 16)); } chunks = Invercargill.single(buffer); endianness = Endianness.Native; } public void append(Enumerable data) { lock(chunks) { chunks = chunks.with(data); } } public void append_all(Enumerable> data) { lock(chunks) { chunks = chunks.concat(data.to_buffer()); } } public void prepend(Enumerable data) { lock(chunks) { chunks = Invercargill.single(data).concat(chunks); } } public void prepend_all(Enumerable> data) { lock(chunks) { chunks = data.concat(chunks.to_buffer()); } } public void append_byte_array(uint8[] data) { append(Wrap.array(data)); } public void append_bytes(Bytes data) { append_byte_array(data.get_data()); } public void append_string(string str) { append_byte_array(get_string_data(str)); } public void prepend_byte_array(uint8[] data) { prepend(Wrap.array(data)); } public void prepend_bytes(Bytes data) { prepend_byte_array(data.get_data()); } public void prepend_string(string str) { prepend_byte_array(get_string_data(str)); } private uint8[] get_string_data(string str) { var data = str.data; return data; } public void push_int64(int64 value) { var chunk = new uint8[sizeof(int64)]; int64 val; switch (endianness) { case Endianness.Native: val = value; break; case Endianness.BigEndian: val = value.to_big_endian(); break; case Endianness.LittleEndian: val = value.to_little_endian(); break; } Memory.copy(chunk, &val, sizeof(int64)); append(Wrap.array(chunk)); } public int64? read_int64(int index) { var data = skip(index).take((int)sizeof(int64)).to_array(); if(data.length != sizeof(int64)) return null; int64 value = 0; int64 val = 0; Memory.copy(&value, data, sizeof(int64)); switch (endianness) { case Endianness.Native: val = value; break; case Endianness.BigEndian: val = value.to_big_endian(); break; case Endianness.LittleEndian: val = value.to_little_endian(); break; } return val; } public void push_all_int64(Enumerable values) { values.iterate(i => push_int64(i)); } public void push_uint64(uint64 value) { var chunk = new uint8[sizeof(uint64)]; uint64 val; switch (endianness) { case Endianness.Native: val = value; break; case Endianness.BigEndian: val = value.to_big_endian(); break; case Endianness.LittleEndian: val = value.to_little_endian(); break; } Memory.copy(chunk, &val, sizeof(uint64)); append(Wrap.array(chunk)); } public uint64? read_uint64(int index) { var data = skip(index).take((int)sizeof(uint64)).to_array(); if(data.length != sizeof(uint64)) return null; uint64 value = 0; uint64 val = 0; Memory.copy(&value, data, sizeof(uint64)); switch (endianness) { case Endianness.Native: val = value; break; case Endianness.BigEndian: val = value.to_big_endian(); break; case Endianness.LittleEndian: val = value.to_little_endian(); break; } return val; } public void push_all_uint64(Enumerable values) { values.iterate(i => push_uint64(i)); } public bool equals(Enumerable other) { return this == other || matches(other, (a, b) => a == b); } public uint hash_code() { return aggregate(5381, (h, b) => h * 33 + b); } public void push_int32(int32 value) { var chunk = new uint8[sizeof(int32)]; int32 val; switch (endianness) { case Endianness.Native: val = value; break; case Endianness.BigEndian: val = value.to_big_endian(); break; case Endianness.LittleEndian: val = value.to_little_endian(); break; } Memory.copy(chunk, &val, sizeof(int32)); append(Wrap.array(chunk)); } public int32? read_int32(int index) { var data = skip(index).take((int)sizeof(int32)).to_array(); if(data.length != sizeof(int32)) return null; int32 value = 0; int32 val = 0; Memory.copy(&value, data, sizeof(int32)); switch (endianness) { case Endianness.Native: val = value; break; case Endianness.BigEndian: val = value.to_big_endian(); break; case Endianness.LittleEndian: val = value.to_little_endian(); break; } return val; } public void push_all_int32(Enumerable values) { values.iterate(i => push_int32(i)); } public void push_uint32(uint32 value) { var chunk = new uint8[sizeof(uint32)]; uint32 val; switch (endianness) { case Endianness.Native: val = value; break; case Endianness.BigEndian: val = value.to_big_endian(); break; case Endianness.LittleEndian: val = value.to_little_endian(); break; } Memory.copy(chunk, &val, sizeof(uint32)); append(Wrap.array(chunk)); } public uint32? read_uint32(int index) { var data = skip(index).take((int)sizeof(uint32)).to_array(); if(data.length != sizeof(uint32)) return null; uint32 value = 0; uint32 val = 0; Memory.copy(&value, data, sizeof(uint32)); switch (endianness) { case Endianness.Native: val = value; break; case Endianness.BigEndian: val = value.to_big_endian(); break; case Endianness.LittleEndian: val = value.to_little_endian(); break; } return val; } public void push_all_uint32(Enumerable values) { values.iterate(i => push_uint32(i)); } public void push_int16(int16 value) { var chunk = new uint8[sizeof(int16)]; int16 val; switch (endianness) { case Endianness.Native: val = value; break; case Endianness.BigEndian: val = value.to_big_endian(); break; case Endianness.LittleEndian: val = value.to_little_endian(); break; } Memory.copy(chunk, &val, sizeof(int16)); append(Wrap.array(chunk)); } public int16? read_int16(int index) { var data = skip(index).take((int)sizeof(int16)).to_array(); if(data.length != sizeof(int16)) return null; int16 value = 0; int16 val = 0; Memory.copy(&value, data, sizeof(int16)); switch (endianness) { case Endianness.Native: val = value; break; case Endianness.BigEndian: val = value.to_big_endian(); break; case Endianness.LittleEndian: val = value.to_little_endian(); break; } return val; } public void push_all_int16(Enumerable values) { values.iterate(i => push_int16(i)); } public void push_uint16(uint16 value) { var chunk = new uint8[sizeof(uint16)]; uint16 val; switch (endianness) { case Endianness.Native: val = value; break; case Endianness.BigEndian: val = value.to_big_endian(); break; case Endianness.LittleEndian: val = value.to_little_endian(); break; } Memory.copy(chunk, &val, sizeof(uint16)); append(Wrap.array(chunk)); } public uint16? read_uint16(int index) { var data = skip(index).take((int)sizeof(uint16)).to_array(); if(data.length != sizeof(uint16)) return null; uint16 value = 0; uint16 val = 0; Memory.copy(&value, data, sizeof(uint16)); switch (endianness) { case Endianness.Native: val = value; break; case Endianness.BigEndian: val = value.to_big_endian(); break; case Endianness.LittleEndian: val = value.to_little_endian(); break; } return val; } public void push_all_uint16(Enumerable values) { values.iterate(i => push_uint16(i)); } public void push_int8(int8 value) { var chunk = new uint8[sizeof(int8)]; Memory.copy(chunk, &value, sizeof(int8)); append(Wrap.array(chunk)); } public int8? read_int8(int index) { var data = skip(index).take((int)sizeof(int8)).to_array(); if(data.length != sizeof(int8)) return null; int8 value = 0; Memory.copy(&value, data, sizeof(int8)); return value; } public void push_all_int8(Enumerable values) { values.iterate(i => push_int8(i)); } public void push_uint8(uint8 value) { var chunk = new uint8[] { value }; append(Wrap.array(chunk)); } public uint8? read_uint8(int index) { var data = skip(index).take((int)sizeof(uint8)).to_array(); if(data.length != sizeof(uint8)) return null; uint8 value = 0; Memory.copy(&value, data, sizeof(uint8)); return value; } public void push_all_uint8(Enumerable values) { values.iterate(i => push_uint8(i)); } public string to_escaped_string() { var builder = new StringBuilder(); foreach (var byte in this) { if(byte >= 32 && byte <= 126) { builder.append_unichar((unichar)byte); } else { builder.append(@"[$(byte)d]"); } } return builder.str; } public string to_raw_string(bool null_terminate = true) { Enumerable data = this; if(null_terminate) { data.concat(Invercargill.single(0)); } return (string)data.to_array(); } public string to_base64() { return Base64.encode(to_array()); } public string to_hex() { return to_string(i => i.to_string("%x")); } public Bytes to_bytes() { return new Bytes(to_array()); } public BinaryData slice(int start, int end) { return new BinaryData.from_enumerable(skip(start).take(end-start)); } public BinaryData read(int start, int length) { return new BinaryData.from_enumerable(skip(start).take(length)); } public bool read_in(Bytes? data, ref size_t amount) throws Error { if(amount <= 0) { return false; } if(data == null) { return true; } if(data.length == 0) { throw new BinaryDataReadError.EMPTY_READ("Encounterd zero-length data object"); } if(data.length > amount) { throw new BinaryDataReadError.BUFFER_OVERFLOW("Encountered more data than requested"); } var bin = new uint8[data.length]; Memory.copy(bin, data.get_data(), data.length); append_byte_array(bin); amount -= data.length; return amount != 0; } public Enumerable wrap(Enumerable enumerable) { chunks = Invercargill.single(enumerable); return this; } public bool can_wrap(Type element_type) { return element_type.is_a(typeof(uint8)); } public size_t write_to(void* array, size_t max_size) { var data = to_array(); var size = max_size > data.length ? data.length : max_size; Memory.copy(array, data, size); return size; } } }