- commit
- 207de367c468cee4f2831a4fbe1f8ede70fd6f0f
- parent
- a7fc7e3af82a0364b358646a990223aa5d50d81b
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2025-08-12 06:19
convert data to binary encoding
Diffstat
| A | cbor.js | 406 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | index.html | 1 | + |
| A | parse.py | 39 | +++++++++++++++++++++++++++++++++++++++ |
| M | sheep.js | 44 | ++++++++++++++------------------------------ |
4 files changed, 460 insertions, 30 deletions
diff --git a/cbor.js b/cbor.js
@@ -0,0 +1,406 @@
-1 1 /*
-1 2 * The MIT License (MIT)
-1 3 *
-1 4 * Copyright (c) 2014 Patrick Gansterer <paroga@paroga.com>
-1 5 *
-1 6 * Permission is hereby granted, free of charge, to any person obtaining a copy
-1 7 * of this software and associated documentation files (the "Software"), to deal
-1 8 * in the Software without restriction, including without limitation the rights
-1 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-1 10 * copies of the Software, and to permit persons to whom the Software is
-1 11 * furnished to do so, subject to the following conditions:
-1 12 *
-1 13 * The above copyright notice and this permission notice shall be included in all
-1 14 * copies or substantial portions of the Software.
-1 15 *
-1 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-1 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-1 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-1 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-1 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-1 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-1 22 * SOFTWARE.
-1 23 */
-1 24
-1 25 (function(global, undefined) { "use strict";
-1 26 var POW_2_24 = Math.pow(2, -24),
-1 27 POW_2_32 = Math.pow(2, 32),
-1 28 POW_2_53 = Math.pow(2, 53);
-1 29
-1 30 function encode(value) {
-1 31 var data = new ArrayBuffer(256);
-1 32 var dataView = new DataView(data);
-1 33 var lastLength;
-1 34 var offset = 0;
-1 35
-1 36 function ensureSpace(length) {
-1 37 var newByteLength = data.byteLength;
-1 38 var requiredLength = offset + length;
-1 39 while (newByteLength < requiredLength)
-1 40 newByteLength *= 2;
-1 41 if (newByteLength !== data.byteLength) {
-1 42 var oldDataView = dataView;
-1 43 data = new ArrayBuffer(newByteLength);
-1 44 dataView = new DataView(data);
-1 45 var uint32count = (offset + 3) >> 2;
-1 46 for (var i = 0; i < uint32count; ++i)
-1 47 dataView.setUint32(i * 4, oldDataView.getUint32(i * 4));
-1 48 }
-1 49
-1 50 lastLength = length;
-1 51 return dataView;
-1 52 }
-1 53 function write() {
-1 54 offset += lastLength;
-1 55 }
-1 56 function writeFloat64(value) {
-1 57 write(ensureSpace(8).setFloat64(offset, value));
-1 58 }
-1 59 function writeUint8(value) {
-1 60 write(ensureSpace(1).setUint8(offset, value));
-1 61 }
-1 62 function writeUint8Array(value) {
-1 63 var dataView = ensureSpace(value.length);
-1 64 for (var i = 0; i < value.length; ++i)
-1 65 dataView.setUint8(offset + i, value[i]);
-1 66 write();
-1 67 }
-1 68 function writeUint16(value) {
-1 69 write(ensureSpace(2).setUint16(offset, value));
-1 70 }
-1 71 function writeUint32(value) {
-1 72 write(ensureSpace(4).setUint32(offset, value));
-1 73 }
-1 74 function writeUint64(value) {
-1 75 var low = value % POW_2_32;
-1 76 var high = (value - low) / POW_2_32;
-1 77 var dataView = ensureSpace(8);
-1 78 dataView.setUint32(offset, high);
-1 79 dataView.setUint32(offset + 4, low);
-1 80 write();
-1 81 }
-1 82 function writeTypeAndLength(type, length) {
-1 83 if (length < 24) {
-1 84 writeUint8(type << 5 | length);
-1 85 } else if (length < 0x100) {
-1 86 writeUint8(type << 5 | 24);
-1 87 writeUint8(length);
-1 88 } else if (length < 0x10000) {
-1 89 writeUint8(type << 5 | 25);
-1 90 writeUint16(length);
-1 91 } else if (length < 0x100000000) {
-1 92 writeUint8(type << 5 | 26);
-1 93 writeUint32(length);
-1 94 } else {
-1 95 writeUint8(type << 5 | 27);
-1 96 writeUint64(length);
-1 97 }
-1 98 }
-1 99
-1 100 function encodeItem(value) {
-1 101 var i;
-1 102
-1 103 if (value === false)
-1 104 return writeUint8(0xf4);
-1 105 if (value === true)
-1 106 return writeUint8(0xf5);
-1 107 if (value === null)
-1 108 return writeUint8(0xf6);
-1 109 if (value === undefined)
-1 110 return writeUint8(0xf7);
-1 111
-1 112 switch (typeof value) {
-1 113 case "number":
-1 114 if (Math.floor(value) === value) {
-1 115 if (0 <= value && value <= POW_2_53)
-1 116 return writeTypeAndLength(0, value);
-1 117 if (-POW_2_53 <= value && value < 0)
-1 118 return writeTypeAndLength(1, -(value + 1));
-1 119 }
-1 120 writeUint8(0xfb);
-1 121 return writeFloat64(value);
-1 122
-1 123 case "string":
-1 124 var utf8data = [];
-1 125 for (i = 0; i < value.length; ++i) {
-1 126 var charCode = value.charCodeAt(i);
-1 127 if (charCode < 0x80) {
-1 128 utf8data.push(charCode);
-1 129 } else if (charCode < 0x800) {
-1 130 utf8data.push(0xc0 | charCode >> 6);
-1 131 utf8data.push(0x80 | charCode & 0x3f);
-1 132 } else if (charCode < 0xd800) {
-1 133 utf8data.push(0xe0 | charCode >> 12);
-1 134 utf8data.push(0x80 | (charCode >> 6) & 0x3f);
-1 135 utf8data.push(0x80 | charCode & 0x3f);
-1 136 } else {
-1 137 charCode = (charCode & 0x3ff) << 10;
-1 138 charCode |= value.charCodeAt(++i) & 0x3ff;
-1 139 charCode += 0x10000;
-1 140
-1 141 utf8data.push(0xf0 | charCode >> 18);
-1 142 utf8data.push(0x80 | (charCode >> 12) & 0x3f);
-1 143 utf8data.push(0x80 | (charCode >> 6) & 0x3f);
-1 144 utf8data.push(0x80 | charCode & 0x3f);
-1 145 }
-1 146 }
-1 147
-1 148 writeTypeAndLength(3, utf8data.length);
-1 149 return writeUint8Array(utf8data);
-1 150
-1 151 default:
-1 152 var length;
-1 153 if (Array.isArray(value)) {
-1 154 length = value.length;
-1 155 writeTypeAndLength(4, length);
-1 156 for (i = 0; i < length; ++i)
-1 157 encodeItem(value[i]);
-1 158 } else if (value instanceof Uint8Array) {
-1 159 writeTypeAndLength(2, value.length);
-1 160 writeUint8Array(value);
-1 161 } else {
-1 162 var keys = Object.keys(value);
-1 163 length = keys.length;
-1 164 writeTypeAndLength(5, length);
-1 165 for (i = 0; i < length; ++i) {
-1 166 var key = keys[i];
-1 167 encodeItem(key);
-1 168 encodeItem(value[key]);
-1 169 }
-1 170 }
-1 171 }
-1 172 }
-1 173
-1 174 encodeItem(value);
-1 175
-1 176 if ("slice" in data)
-1 177 return data.slice(0, offset);
-1 178
-1 179 var ret = new ArrayBuffer(offset);
-1 180 var retView = new DataView(ret);
-1 181 for (var i = 0; i < offset; ++i)
-1 182 retView.setUint8(i, dataView.getUint8(i));
-1 183 return ret;
-1 184 }
-1 185
-1 186 function decode(data, tagger, simpleValue) {
-1 187 var dataView = new DataView(data);
-1 188 var offset = 0;
-1 189
-1 190 if (typeof tagger !== "function")
-1 191 tagger = function(value) { return value; };
-1 192 if (typeof simpleValue !== "function")
-1 193 simpleValue = function() { return undefined; };
-1 194
-1 195 function read(value, length) {
-1 196 offset += length;
-1 197 return value;
-1 198 }
-1 199 function readArrayBuffer(length) {
-1 200 return read(new Uint8Array(data, offset, length), length);
-1 201 }
-1 202 function readFloat16() {
-1 203 var tempArrayBuffer = new ArrayBuffer(4);
-1 204 var tempDataView = new DataView(tempArrayBuffer);
-1 205 var value = readUint16();
-1 206
-1 207 var sign = value & 0x8000;
-1 208 var exponent = value & 0x7c00;
-1 209 var fraction = value & 0x03ff;
-1 210
-1 211 if (exponent === 0x7c00)
-1 212 exponent = 0xff << 10;
-1 213 else if (exponent !== 0)
-1 214 exponent += (127 - 15) << 10;
-1 215 else if (fraction !== 0)
-1 216 return fraction * POW_2_24;
-1 217
-1 218 tempDataView.setUint32(0, sign << 16 | exponent << 13 | fraction << 13);
-1 219 return tempDataView.getFloat32(0);
-1 220 }
-1 221 function readFloat32() {
-1 222 return read(dataView.getFloat32(offset), 4);
-1 223 }
-1 224 function readFloat64() {
-1 225 return read(dataView.getFloat64(offset), 8);
-1 226 }
-1 227 function readUint8() {
-1 228 return read(dataView.getUint8(offset), 1);
-1 229 }
-1 230 function readUint16() {
-1 231 return read(dataView.getUint16(offset), 2);
-1 232 }
-1 233 function readUint32() {
-1 234 return read(dataView.getUint32(offset), 4);
-1 235 }
-1 236 function readUint64() {
-1 237 return readUint32() * POW_2_32 + readUint32();
-1 238 }
-1 239 function readBreak() {
-1 240 if (dataView.getUint8(offset) !== 0xff)
-1 241 return false;
-1 242 offset += 1;
-1 243 return true;
-1 244 }
-1 245 function readLength(additionalInformation) {
-1 246 if (additionalInformation < 24)
-1 247 return additionalInformation;
-1 248 if (additionalInformation === 24)
-1 249 return readUint8();
-1 250 if (additionalInformation === 25)
-1 251 return readUint16();
-1 252 if (additionalInformation === 26)
-1 253 return readUint32();
-1 254 if (additionalInformation === 27)
-1 255 return readUint64();
-1 256 if (additionalInformation === 31)
-1 257 return -1;
-1 258 throw "Invalid length encoding";
-1 259 }
-1 260 function readIndefiniteStringLength(majorType) {
-1 261 var initialByte = readUint8();
-1 262 if (initialByte === 0xff)
-1 263 return -1;
-1 264 var length = readLength(initialByte & 0x1f);
-1 265 if (length < 0 || (initialByte >> 5) !== majorType)
-1 266 throw "Invalid indefinite length element";
-1 267 return length;
-1 268 }
-1 269
-1 270 function appendUtf16data(utf16data, length) {
-1 271 for (var i = 0; i < length; ++i) {
-1 272 var value = readUint8();
-1 273 if (value & 0x80) {
-1 274 if (value < 0xe0) {
-1 275 value = (value & 0x1f) << 6
-1 276 | (readUint8() & 0x3f);
-1 277 length -= 1;
-1 278 } else if (value < 0xf0) {
-1 279 value = (value & 0x0f) << 12
-1 280 | (readUint8() & 0x3f) << 6
-1 281 | (readUint8() & 0x3f);
-1 282 length -= 2;
-1 283 } else {
-1 284 value = (value & 0x0f) << 18
-1 285 | (readUint8() & 0x3f) << 12
-1 286 | (readUint8() & 0x3f) << 6
-1 287 | (readUint8() & 0x3f);
-1 288 length -= 3;
-1 289 }
-1 290 }
-1 291
-1 292 if (value < 0x10000) {
-1 293 utf16data.push(value);
-1 294 } else {
-1 295 value -= 0x10000;
-1 296 utf16data.push(0xd800 | (value >> 10));
-1 297 utf16data.push(0xdc00 | (value & 0x3ff));
-1 298 }
-1 299 }
-1 300 }
-1 301
-1 302 function decodeItem() {
-1 303 var initialByte = readUint8();
-1 304 var majorType = initialByte >> 5;
-1 305 var additionalInformation = initialByte & 0x1f;
-1 306 var i;
-1 307 var length;
-1 308
-1 309 if (majorType === 7) {
-1 310 switch (additionalInformation) {
-1 311 case 25:
-1 312 return readFloat16();
-1 313 case 26:
-1 314 return readFloat32();
-1 315 case 27:
-1 316 return readFloat64();
-1 317 }
-1 318 }
-1 319
-1 320 length = readLength(additionalInformation);
-1 321 if (length < 0 && (majorType < 2 || 6 < majorType))
-1 322 throw "Invalid length";
-1 323
-1 324 switch (majorType) {
-1 325 case 0:
-1 326 return length;
-1 327 case 1:
-1 328 return -1 - length;
-1 329 case 2:
-1 330 if (length < 0) {
-1 331 var elements = [];
-1 332 var fullArrayLength = 0;
-1 333 while ((length = readIndefiniteStringLength(majorType)) >= 0) {
-1 334 fullArrayLength += length;
-1 335 elements.push(readArrayBuffer(length));
-1 336 }
-1 337 var fullArray = new Uint8Array(fullArrayLength);
-1 338 var fullArrayOffset = 0;
-1 339 for (i = 0; i < elements.length; ++i) {
-1 340 fullArray.set(elements[i], fullArrayOffset);
-1 341 fullArrayOffset += elements[i].length;
-1 342 }
-1 343 return fullArray;
-1 344 }
-1 345 return readArrayBuffer(length);
-1 346 case 3:
-1 347 var utf16data = [];
-1 348 if (length < 0) {
-1 349 while ((length = readIndefiniteStringLength(majorType)) >= 0)
-1 350 appendUtf16data(utf16data, length);
-1 351 } else
-1 352 appendUtf16data(utf16data, length);
-1 353 return String.fromCharCode.apply(null, utf16data);
-1 354 case 4:
-1 355 var retArray;
-1 356 if (length < 0) {
-1 357 retArray = [];
-1 358 while (!readBreak())
-1 359 retArray.push(decodeItem());
-1 360 } else {
-1 361 retArray = new Array(length);
-1 362 for (i = 0; i < length; ++i)
-1 363 retArray[i] = decodeItem();
-1 364 }
-1 365 return retArray;
-1 366 case 5:
-1 367 var retObject = {};
-1 368 for (i = 0; i < length || length < 0 && !readBreak(); ++i) {
-1 369 var key = decodeItem();
-1 370 retObject[key] = decodeItem();
-1 371 }
-1 372 return retObject;
-1 373 case 6:
-1 374 return tagger(decodeItem(), length);
-1 375 case 7:
-1 376 switch (length) {
-1 377 case 20:
-1 378 return false;
-1 379 case 21:
-1 380 return true;
-1 381 case 22:
-1 382 return null;
-1 383 case 23:
-1 384 return undefined;
-1 385 default:
-1 386 return simpleValue(length);
-1 387 }
-1 388 }
-1 389 }
-1 390
-1 391 var ret = decodeItem();
-1 392 if (offset !== data.byteLength)
-1 393 throw "Remaining bytes";
-1 394 return ret;
-1 395 }
-1 396
-1 397 var obj = { encode: encode, decode: decode };
-1 398
-1 399 if (typeof define === "function" && define.amd)
-1 400 define("cbor/cbor", obj);
-1 401 else if (typeof module !== 'undefined' && module.exports)
-1 402 module.exports = obj;
-1 403 else if (!global.CBOR)
-1 404 global.CBOR = obj;
-1 405
-1 406 })(this);
diff --git a/index.html b/index.html
@@ -46,5 +46,6 @@ 46 46 </ul> 47 47 </dialog> 48 48 -1 49 <script src="cbor.js"></script> 49 50 <script type="module" src="sheep.js"></script> 50 51 </body>
diff --git a/parse.py b/parse.py
@@ -0,0 +1,39 @@
-1 1 import sys
-1 2
-1 3 import cbor
-1 4
-1 5
-1 6 def parse_url(s):
-1 7 data = []
-1 8 parts = s.strip().lstrip('?').split('&')
-1 9 for part in parts:
-1 10 key, value = part.split('=', 1)
-1 11 if key in ['xOff', 'yOff']:
-1 12 data.append(round(float(value or '0') * 2))
-1 13 else:
-1 14 assert key == 'drawing'
-1 15 for cmd in value.split('_'):
-1 16 cmd = cmd.split('.')
-1 17 if cmd[0] == 'lift':
-1 18 data.append([cmd[0]])
-1 19 elif cmd[0] in ['stroke', 'grey']:
-1 20 data.append([cmd[0], int(cmd[1], 10)])
-1 21 elif cmd[0].isdigit():
-1 22 data.append([int(x, 10) for x in cmd])
-1 23 return data
-1 24
-1 25
-1 26 def dump_url(data):
-1 27 cmds = '_'.join('.'.join(str(x) for x in cmd) for cmd in data[2:])
-1 28 return f'yOff={data[0] / 2}&xOff={data[1] / 2}&drawing={"_".join(cmds)}'
-1 29
-1 30
-1 31 if __name__ == '__main__':
-1 32 for k in range(0, 100):
-1 33 items = []
-1 34 for i in range(k * 100, (k + 1) * 100):
-1 35 print(i + 1, end='\r', file=sys.stderr)
-1 36 with open(f'data/{i + 1}') as fh:
-1 37 items.append(parse_url(fh.read()))
-1 38 with open(f'bin/{k}', 'wb') as fh:
-1 39 cbor.dump(items, fh)
diff --git a/sheep.js b/sheep.js
@@ -12,19 +12,6 @@ var hoverCol = 0; 12 12 var hoverRow = 0; 13 13 14 1415 -1 var parseQuery = function(query) {16 -1 query = query.trim();17 -1 if (query[0] === '?') {18 -1 query = query.substr(1);19 -1 }20 -1 var ret = {};21 -1 query.split('&').forEach(part => {22 -1 var a = part.split('=');23 -1 ret[a[0]] = a[1];24 -1 });25 -1 return ret;26 -1 };27 -128 15 var grayToColor = function(gray) { 29 16 var c = ('00' + gray.toString(16)).substr(-2); 30 17 return '#' + c + c + c; @@ -47,23 +34,20 @@ var eachWithTimeout = function(array, fn, timeout) { 47 34 }; 48 35 49 36 var drawSheep = function(sheep) {50 -1 var xOff = parseFloat(sheep.xOff, 10);51 -1 var yOff = parseFloat(sheep.yOff, 10);-1 37 var yOff = sheep[0] / 2; -1 38 var xOff = sheep[1] / 2; 52 39 53 40 ctx.beginPath();54 -1 return eachWithTimeout(sheep.drawing.split('_'), s => {55 -1 var cmd = s.split('.');56 -1-1 41 return eachWithTimeout(sheep.slice(2), cmd => { 57 42 if (cmd[0] === 'lift') { 58 43 ctx.beginPath(); 59 44 } else if (cmd[0] === 'stroke') {60 -1 ctx.lineWidth = parseInt(cmd[1], 10);-1 45 ctx.lineWidth = cmd[1]; 61 46 } else if (cmd[0] === 'grey') {62 -1 ctx.strokeStyle = grayToColor(parseInt(cmd[1], 10));63 -1 } else if (parseInt(cmd[0], 10)) {64 -1 var coords = cmd.map(x => parseInt(x, 10));65 -1 ctx.moveTo(coords[2] + xOff, coords[3] + yOff);66 -1 ctx.lineTo(coords[0] + xOff, coords[1] + yOff);-1 47 ctx.strokeStyle = grayToColor(cmd[1]); -1 48 } else { -1 49 ctx.moveTo(cmd[2] + xOff, cmd[3] + yOff); -1 50 ctx.lineTo(cmd[0] + xOff, cmd[1] + yOff); 67 51 ctx.stroke(); 68 52 } 69 53 }, 10); @@ -110,13 +94,13 @@ grid.addEventListener('keydown', event => { 110 94 } 111 95 }); 112 96113 -1 var q = parseQuery(location.search);114 -1 var id = parseInt(q.sheep, 10);-1 97 var q = new URLSearchParams(location.search); -1 98 var id = parseInt(q.get('sheep'), 10); 115 99 if (id) {116 -1 fetch('data/' + id)117 -1 .then(r => r.text())118 -1 .then(parseQuery)119 -1 .then(drawSheep);-1 100 var response = await fetch(`bin/${Math.floor((id - 1) / 100)}`); -1 101 var data = CBOR.decode(await response.arrayBuffer()); -1 102 var sheep = data[(id - 1) % 100]; -1 103 drawSheep(sheep); 120 104 121 105 hoverRow = (id - 1) % rows; 122 106 hoverCol = Math.floor((id - 1) / rows);