/* This code is hereby released to the public domain. ~aaaaaa123456789, 2015-07-16 */ using System; using System.Collections.Generic; public static class IntegerSerializer { public static byte[] serialize (UnlimitedMatrix data) { List contents = new List(); ulong size = 0; long value; long current_value = 0; List current_stack = new List(); long[] previous = new long[] {64, 64}; foreach (long[] coords in data) { value = convert_from(data[coords[0], coords[1]]); if (value == current_value) { current_stack.Add(coords); continue; } if (current_value != 0) write_stack(contents, ref size, current_stack, current_value, ref previous); current_value = value; current_stack = new List(); current_stack.Add(coords); } if (current_value != 0) write_stack(contents, ref size, current_stack, current_value, ref previous); current_stack = null; byte[] result = new byte[size + 1]; ulong current, pointer = 0; for (current = 0; current < (ulong) contents.Count; current ++) { Array.Copy(contents[(int) current], 0L, result, (long) pointer, contents[(int) current].LongLength); pointer += (ulong) contents[(int) current].LongLength; } result[pointer] = 0; return result; } private static void write_stack (List contents, ref ulong size, List stack, long value, ref long[] prev) { if (stack.Count < 4) write_stack_single(contents, ref size, stack, value, ref prev); else write_stack_run(contents, ref size, stack, value, ref prev); } private static void write_stack_single (List contents, ref ulong size, List stack, long value, ref long[] prev) { byte[] serialized_value = encode_number(value, false); foreach(long[] coords in stack) { push(contents, ref size, serialized_value); push_coordinates(contents, ref size, coords, ref prev); } } private static void write_stack_run (List contents, ref ulong size, List stack, long value, ref long[] prev) { push(contents, ref size, encode_number(value, true)); foreach (long[] coords in stack) push_coordinates(contents, ref size, coords, ref prev); push(contents, ref size, new byte[] {0x40, 0x40}); } private static void push_coordinates (List contents, ref ulong size, long[] coordinates, ref long[] previous) { push(contents, ref size, create_coordinate(coordinates[0], ref previous[0])); push(contents, ref size, create_coordinate(coordinates[1], ref previous[1])); } private static byte[] create_coordinate (long coordinate, ref long reference) { byte[] absolute = encode_number(coordinate); byte[] relative = encode_number(coordinate, reference); reference = coordinate; if (relative.Length < absolute.Length) return relative; return absolute; } private static void push (List contents, ref ulong size, byte[] item) { contents.Add(item); size += (ulong) item.LongLength; } public static UnlimitedMatrix unserialize (byte[] data, ref ulong pointer) { UnlimitedMatrix result = new UnlimitedMatrix(); long[] previous_coordinates = new long[] {64, 64}; while (data[pointer] != 0) if ((data[pointer] & 0x40) != 0) read_run(result, data, ref pointer, ref previous_coordinates); else read_single(result, data, ref pointer, ref previous_coordinates); pointer ++; return result; } private static void read_single (UnlimitedMatrix dst, byte[] src, ref ulong pointer, ref long[] previous) { bool kind; int advance; long value = decode_number(src, pointer, out kind, out advance); if (kind) throw new InvalidOperationException(); pointer += (ulong) advance; long[] coordinates = read_coordinates(src, ref pointer, ref previous); dst[coordinates[0], coordinates[1]] = convert_to(value); } private static void read_run (UnlimitedMatrix dst, byte[] src, ref ulong pointer, ref long[] previous) { bool kind; int advance; long value = decode_number(src, pointer, out kind, out advance); T converted = convert_to(value); if (!kind) throw new InvalidOperationException(); pointer += (ulong) advance; long[] coordinates; while ((src[pointer] != 0x40) || (src[pointer + 1] != 0x40)) { coordinates = read_coordinates(src, ref pointer, ref previous); dst[coordinates[0], coordinates[1]] = converted; } pointer += 2; } private static long[] read_coordinates (byte[] src, ref ulong pointer, ref long[] previous) { long[] result = new long[2]; result[0] = read_coordinate(src, ref pointer, ref previous[0]); result[1] = read_coordinate(src, ref pointer, ref previous[1]); return result; } private static long read_coordinate (byte[] src, ref ulong pointer, ref long previous) { bool relative; int advance; long coordinate = decode_number(src, pointer, out relative, out advance); pointer += (ulong) advance; if (relative) coordinate += previous; previous = coordinate; return coordinate; } public static UnlimitedMatrix unserialize (byte[] data, ulong pointer) { return unserialize(data, ref pointer); } public static UnlimitedMatrix unserialize (byte[] data) { ulong pointer = 0; return unserialize(data, ref pointer); } private static byte[] encode_number (long number, bool kind) { bool sign = number < 0; if (sign) number = ~number; int length; if (number < 0x20L) length = 1; else if (number < 0x1000L) length = 2; else if (number < 0x80000L) length = 3; else if (number < 0x4000000L) length = 4; else if (number < 0x200000000L) length = 5; else if (number < 0x10000000000L) length = 6; else if (number < 0x800000000000L) length = 7; else length = 9; byte[] result = new byte[length]; long available_bits = 0x887777775L; int current, bits; for (current = 0; current < length; current ++) { bits = (int) (available_bits & 15); available_bits >>= 4; result[current] = (byte) ((((bits == 8) || (length == (current + 1))) ? 0 : 0x80) | ((byte) number & ((1 << bits) - 1))); number >>= bits; } if (kind) result[0] |= 0x40; if (sign) result[0] |= 0x20; return result; } private static byte[] encode_number (long number, long reference) { return encode_number(number - reference, true); } private static byte[] encode_number (long number) { return encode_number(number, false); } private static long decode_number (byte[] data, ulong position, out bool kind, out int length) { for (length = 0; length < 7; length ++) if ((data[position + (ulong) length] & 0x80) == 0) break; if (length == 7) length = 9; else length ++; if ((ulong) data.Length < (position + (ulong) length)) throw new IndexOutOfRangeException(); long available_bits = 0x887777775L; int current, bits, shift_state = 0; long result = 0; for (current = 0; current < length; current ++) { bits = (int) (available_bits & 15); available_bits >>= 4; result |= (((long) data[position + (ulong) current]) & ((1L << bits) - 1)) << shift_state; shift_state += bits; } kind = (data[position] & 0x40) != 0; if ((data[position] & 0x20) != 0) result = ~result; return result; } private static long decode_number (byte[] data, ulong position, long reference, out int length) { bool relative; long rv = decode_number(data, position, out relative, out length); if (relative) rv += reference; return rv; } private static long convert_from (T value) { return (long) Convert.ChangeType(value, typeof(long)); } private static T convert_to (long value) { return (T) Convert.ChangeType(value, typeof(T)); } }