1 |
|
2 |
|
3 |
|
4 |
|
5 | CND = require 'cnd'
|
6 | rpr = CND.rpr
|
7 | badge = 'HOLLERITH-CODEC/MAIN'
|
8 | debug = CND.get_logger 'debug', badge
|
9 | warn = CND.get_logger 'warn', badge
|
10 |
|
11 | @types = require './types'
|
12 | { isa
|
13 | validate
|
14 | cast
|
15 | declare
|
16 | size_of
|
17 | type_of } = @types
|
18 | VOID = Symbol 'VOID'
|
19 |
|
20 |
|
21 |
|
22 | @[ 'typemarkers' ] = {}
|
23 |
|
24 | tm_lo = @[ 'typemarkers' ][ 'lo' ] = 0x00
|
25 | tm_null = @[ 'typemarkers' ][ 'null' ] = 'B'.codePointAt 0
|
26 | tm_false = @[ 'typemarkers' ][ 'false' ] = 'C'.codePointAt 0
|
27 | tm_true = @[ 'typemarkers' ][ 'true' ] = 'D'.codePointAt 0
|
28 | tm_list = @[ 'typemarkers' ][ 'list' ] = 'E'.codePointAt 0
|
29 | tm_date = @[ 'typemarkers' ][ 'date' ] = 'G'.codePointAt 0
|
30 | tm_ninfinity = @[ 'typemarkers' ][ 'ninfinity' ] = 'J'.codePointAt 0
|
31 | tm_nnumber = @[ 'typemarkers' ][ 'nnumber' ] = 'K'.codePointAt 0
|
32 | tm_void = @[ 'typemarkers' ][ 'void' ] = 'L'.codePointAt 0
|
33 | tm_pnumber = @[ 'typemarkers' ][ 'pnumber' ] = 'M'.codePointAt 0
|
34 | tm_pinfinity = @[ 'typemarkers' ][ 'pinfinity' ] = 'N'.codePointAt 0
|
35 | tm_text = @[ 'typemarkers' ][ 'text' ] = 'T'.codePointAt 0
|
36 | tm_private = @[ 'typemarkers' ][ 'private' ] = 'Z'.codePointAt 0
|
37 | tm_hi = @[ 'typemarkers' ][ 'hi' ] = 0xff
|
38 |
|
39 |
|
40 | @[ 'bytecounts' ] = {}
|
41 | bytecount_singular = @[ 'bytecounts' ][ 'singular' ] = 1
|
42 | bytecount_typemarker = @[ 'bytecounts' ][ 'typemarker' ] = 1
|
43 | bytecount_float = @[ 'bytecounts' ][ 'float' ] = 9
|
44 | bytecount_date = @[ 'bytecounts' ][ 'date' ] = bytecount_float + 1
|
45 |
|
46 |
|
47 | @[ 'sentinels' ] = {}
|
48 |
|
49 |
|
50 | @[ 'sentinels' ][ 'firstdate' ] = new Date -8640000000000000
|
51 | @[ 'sentinels' ][ 'lastdate' ] = new Date +8640000000000000
|
52 |
|
53 |
|
54 | @[ 'keys' ] = {}
|
55 |
|
56 | @[ 'keys' ][ 'lo' ] = Buffer.alloc 1, @[ 'typemarkers' ][ 'lo' ]
|
57 | @[ 'keys' ][ 'hi' ] = Buffer.alloc 1, @[ 'typemarkers' ][ 'hi' ]
|
58 |
|
59 |
|
60 | @[ 'symbols' ] = {}
|
61 | symbol_fallback = @[ 'fallback' ] = Symbol 'fallback'
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 | rbuffer_min_size = 1024
|
68 | rbuffer_max_size = 65536
|
69 | rbuffer = Buffer.alloc rbuffer_min_size
|
70 |
|
71 |
|
72 | grow_rbuffer = ->
|
73 | factor = 2
|
74 | new_size = Math.floor rbuffer.length * factor + 0.5
|
75 |
|
76 | new_result_buffer = Buffer.alloc new_size
|
77 | rbuffer.copy new_result_buffer
|
78 | rbuffer = new_result_buffer
|
79 | return null
|
80 |
|
81 |
|
82 | release_extraneous_rbuffer_bytes = ->
|
83 | if rbuffer.length > rbuffer_max_size
|
84 |
|
85 | rbuffer = Buffer.alloc rbuffer_max_size
|
86 | return null
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 | @write_singular = ( idx, value ) ->
|
93 | grow_rbuffer() until rbuffer.length >= idx + bytecount_singular
|
94 | if value is null then typemarker = tm_null
|
95 | else if value is false then typemarker = tm_false
|
96 | else if value is true then typemarker = tm_true
|
97 | else if value is VOID then typemarker = tm_void
|
98 | else throw new Error "µ56733 unable to encode value of type #{type_of value}"
|
99 | rbuffer[ idx ] = typemarker
|
100 | return idx + bytecount_singular
|
101 |
|
102 |
|
103 | @read_singular = ( buffer, idx ) ->
|
104 | switch typemarker = buffer[ idx ]
|
105 | when tm_null then value = null
|
106 | when tm_false then value = false
|
107 | when tm_true then value = true
|
108 |
|
109 | when tm_void then value = VOID
|
110 | else throw new Error "µ57564 unable to decode 0x#{typemarker.toString 16} at index #{idx} (#{rpr buffer})"
|
111 | return [ idx + bytecount_singular, value, ]
|
112 |
|
113 |
|
114 |
|
115 |
|
116 |
|
117 | @write_private = ( idx, value, encoder ) ->
|
118 | grow_rbuffer() until rbuffer.length >= idx + 3 * bytecount_typemarker
|
119 |
|
120 | rbuffer[ idx ] = tm_private
|
121 | idx += bytecount_typemarker
|
122 |
|
123 | rbuffer[ idx ] = tm_list
|
124 | idx += bytecount_typemarker
|
125 |
|
126 | type = value[ 'type' ] ? 'private'
|
127 | proper_value = value[ 'value' ]
|
128 |
|
129 | if encoder?
|
130 | encoded_value = encoder type, proper_value, symbol_fallback
|
131 | proper_value = encoded_value unless encoded_value is symbol_fallback
|
132 |
|
133 | else if type.startsWith '-'
|
134 |
|
135 | switch type
|
136 | when '-set'
|
137 | null
|
138 | else
|
139 | throw new Error "µ58395 unknown built-in private type #{rpr type}"
|
140 |
|
141 | wrapped_value = [ type, proper_value, ]
|
142 | idx = @_encode wrapped_value, idx
|
143 |
|
144 | rbuffer[ idx ] = tm_lo
|
145 | idx += bytecount_typemarker
|
146 |
|
147 | return idx
|
148 |
|
149 |
|
150 | @read_private = ( buffer, idx, decoder ) ->
|
151 | idx += bytecount_typemarker
|
152 | [ idx, [ type, value, ] ] = @read_list buffer, idx
|
153 |
|
154 | if decoder?
|
155 | R = decoder type, value, symbol_fallback
|
156 | throw new Error "µ59226 encountered illegal value `undefined` when reading private type" if R is undefined
|
157 | R = { type, value, } if R is symbol_fallback
|
158 |
|
159 | else if type.startsWith '-'
|
160 |
|
161 | switch type
|
162 | when '-set'
|
163 |
|
164 | R = new Set value[ 0 ]
|
165 | else
|
166 | throw new Error "µ60057 unknown built-in private type #{rpr type}"
|
167 |
|
168 | else
|
169 | R = { type, value, }
|
170 | return [ idx, R, ]
|
171 |
|
172 |
|
173 |
|
174 |
|
175 |
|
176 | @write_number = ( idx, number ) ->
|
177 | grow_rbuffer() until rbuffer.length >= idx + bytecount_float
|
178 | if number < 0
|
179 | type = tm_nnumber
|
180 | number = -number
|
181 | else
|
182 | type = tm_pnumber
|
183 | rbuffer[ idx ] = type
|
184 | rbuffer.writeDoubleBE number, idx + 1
|
185 | @_invert_buffer rbuffer, idx if type is tm_nnumber
|
186 | return idx + bytecount_float
|
187 |
|
188 |
|
189 | @write_infinity = ( idx, number ) ->
|
190 | grow_rbuffer() until rbuffer.length >= idx + bytecount_singular
|
191 | rbuffer[ idx ] = if number is -Infinity then tm_ninfinity else tm_pinfinity
|
192 | return idx + bytecount_singular
|
193 |
|
194 |
|
195 | @read_nnumber = ( buffer, idx ) ->
|
196 | throw new Error "µ60888 not a negative number at index #{idx}" unless buffer[ idx ] is tm_nnumber
|
197 | copy = @_invert_buffer ( Buffer.from buffer.slice idx, idx + bytecount_float ), 0
|
198 | return [ idx + bytecount_float, -( copy.readDoubleBE 1 ), ]
|
199 |
|
200 |
|
201 | @read_pnumber = ( buffer, idx ) ->
|
202 | throw new Error "µ61719 not a positive number at index #{idx}" unless buffer[ idx ] is tm_pnumber
|
203 | return [ idx + bytecount_float, buffer.readDoubleBE idx + 1, ]
|
204 |
|
205 |
|
206 | @_invert_buffer = ( buffer, idx ) ->
|
207 | buffer[ i ] = ~buffer[ i ] for i in [ idx + 1 .. idx + 8 ]
|
208 | return buffer
|
209 |
|
210 |
|
211 |
|
212 |
|
213 |
|
214 | @write_date = ( idx, date ) ->
|
215 | grow_rbuffer() until rbuffer.length >= idx + bytecount_date
|
216 | number = +date
|
217 | rbuffer[ idx ] = tm_date
|
218 | return @write_number idx + 1, number
|
219 |
|
220 |
|
221 | @read_date = ( buffer, idx ) ->
|
222 | throw new Error "µ62550 not a date at index #{idx}" unless buffer[ idx ] is tm_date
|
223 | switch type = buffer[ idx + 1 ]
|
224 | when tm_nnumber then [ idx, value, ] = @read_nnumber buffer, idx + 1
|
225 | when tm_pnumber then [ idx, value, ] = @read_pnumber buffer, idx + 1
|
226 | else throw new Error "µ63381 unknown date type marker 0x#{type.toString 16} at index #{idx}"
|
227 | return [ idx, ( new Date value ), ]
|
228 |
|
229 |
|
230 |
|
231 |
|
232 |
|
233 | @write_text = ( idx, text ) ->
|
234 | text = text.replace /\x01/g, '\x01\x02'
|
235 | text = text.replace /\x00/g, '\x01\x01'
|
236 | bytecount_text = ( Buffer.byteLength text, 'utf-8' ) + 2
|
237 | grow_rbuffer() until rbuffer.length >= idx + bytecount_text
|
238 | rbuffer[ idx ] = tm_text
|
239 | rbuffer.write text, idx + 1
|
240 | rbuffer[ idx + bytecount_text - 1 ] = tm_lo
|
241 | return idx + bytecount_text
|
242 |
|
243 |
|
244 | @read_text = ( buffer, idx ) ->
|
245 |
|
246 | throw new Error "µ64212 not a text at index #{idx}" unless buffer[ idx ] is tm_text
|
247 | stop_idx = idx
|
248 | loop
|
249 | stop_idx += +1
|
250 | break if ( byte = buffer[ stop_idx ] ) is tm_lo
|
251 | throw new Error "µ65043 runaway string at index #{idx}" unless byte?
|
252 | R = buffer.toString 'utf-8', idx + 1, stop_idx
|
253 | R = R.replace /\x01\x01/g, '\x00'
|
254 | R = R.replace /\x01\x02/g, '\x01'
|
255 | return [ stop_idx + 1, R, ]
|
256 |
|
257 |
|
258 |
|
259 |
|
260 |
|
261 | @read_list = ( buffer, idx ) ->
|
262 | throw new Error "µ65874 not a list at index #{idx}" unless buffer[ idx ] is tm_list
|
263 | R = []
|
264 | idx += +1
|
265 | loop
|
266 | break if ( byte = buffer[ idx ] ) is tm_lo
|
267 | [ idx, value, ] = @_decode buffer, idx, true
|
268 | R.push value[ 0 ]
|
269 | throw new Error "µ66705 runaway list at index #{idx}" unless byte?
|
270 | return [ idx + 1, R, ]
|
271 |
|
272 |
|
273 |
|
274 |
|
275 |
|
276 | @write = ( idx, value, encoder ) ->
|
277 | return @write_singular idx, value if value is VOID
|
278 | switch type = type_of value
|
279 | when 'text' then return @write_text idx, value
|
280 | when 'float' then return @write_number idx, value
|
281 | when 'infinity' then return @write_infinity idx, value
|
282 | when 'date' then return @write_date idx, value
|
283 |
|
284 | when 'set'
|
285 |
|
286 | return @write_private idx, { type: '-set', value: [ ( Array.from value ), ], }
|
287 |
|
288 | return @write_private idx, value, encoder if isa.object value
|
289 | return @write_singular idx, value
|
290 |
|
291 |
|
292 |
|
293 |
|
294 |
|
295 | @encode = ( key, encoder ) ->
|
296 | key = key[ .. ]
|
297 | key.push VOID
|
298 | rbuffer.fill 0x00
|
299 | throw new Error "µ67536 expected a list, got a #{type}" unless ( type = type_of key ) is 'list'
|
300 | idx = @_encode key, 0, encoder
|
301 | R = Buffer.alloc idx
|
302 | rbuffer.copy R, 0, 0, idx
|
303 | release_extraneous_rbuffer_bytes()
|
304 |
|
305 | return R
|
306 |
|
307 |
|
308 | @encode_plus_hi = ( key, encoder ) ->
|
309 |
|
310 | rbuffer.fill 0x00
|
311 | throw new Error "µ68367 expected a list, got a #{type}" unless ( type = type_of key ) is 'list'
|
312 | idx = @_encode key, 0, encoder
|
313 | grow_rbuffer() until rbuffer.length >= idx + 1
|
314 | rbuffer[ idx ] = tm_hi
|
315 | idx += +1
|
316 | R = Buffer.alloc idx
|
317 | rbuffer.copy R, 0, 0, idx
|
318 | release_extraneous_rbuffer_bytes()
|
319 |
|
320 | return R
|
321 |
|
322 |
|
323 | @_encode = ( key, idx, encoder ) ->
|
324 | last_element_idx = key.length - 1
|
325 | for element, element_idx in key
|
326 | try
|
327 | if isa.list element
|
328 | rbuffer[ idx ] = tm_list
|
329 | idx += +1
|
330 | for sub_element in element
|
331 | idx = @_encode [ sub_element, ], idx, encoder
|
332 | rbuffer[ idx ] = tm_lo
|
333 | idx += +1
|
334 | else
|
335 | idx = @write idx, element, encoder
|
336 | catch error
|
337 | key_rpr = []
|
338 | for element in key
|
339 | if isa.buffer element
|
340 | throw new Error "µ45533 unable to encode buffers"
|
341 |
|
342 | else
|
343 | key_rpr.push rpr element
|
344 | warn "µ44544 detected problem with key [ #{rpr key_rpr.join ', '} ]"
|
345 | throw error
|
346 |
|
347 | return idx
|
348 |
|
349 |
|
350 | @decode = ( buffer, decoder ) ->
|
351 |
|
352 | buffer = buffer.slice 0, buffer.length - 1
|
353 | return ( @_decode buffer, 0, false, decoder )[ 1 ]
|
354 |
|
355 |
|
356 | @_decode = ( buffer, idx, single, decoder ) ->
|
357 | R = []
|
358 | last_idx = buffer.length - 1
|
359 | loop
|
360 | break if idx > last_idx
|
361 | switch type = buffer[ idx ]
|
362 | when tm_list then [ idx, value, ] = @read_list buffer, idx
|
363 | when tm_text then [ idx, value, ] = @read_text buffer, idx
|
364 | when tm_nnumber then [ idx, value, ] = @read_nnumber buffer, idx
|
365 | when tm_ninfinity then [ idx, value, ] = [ idx + 1, -Infinity, ]
|
366 | when tm_pnumber then [ idx, value, ] = @read_pnumber buffer, idx
|
367 | when tm_pinfinity then [ idx, value, ] = [ idx + 1, +Infinity, ]
|
368 | when tm_date then [ idx, value, ] = @read_date buffer, idx
|
369 | when tm_private then [ idx, value, ] = @read_private buffer, idx, decoder
|
370 | else [ idx, value, ] = @read_singular buffer, idx
|
371 | R.push value
|
372 | break if single
|
373 |
|
374 | return [ idx, R ]
|
375 |
|
376 |
|
377 |
|
378 |
|
379 |
|
380 |
|
381 |
|
382 |
|
383 | @encodings =
|
384 |
|
385 |
|
386 | dbcs2: """
|
387 | ⓪①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛
|
388 | ㉜!"#$%&'()*+,-./0123456789:;<=>?
|
389 | @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
|
390 | `abcdefghijklmnopqrstuvwxyz{|}~㉠
|
391 | ㉝㉞㉟㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿㋐㋑㋒㋓㋔㋕㋖㋗㋘㋙㋚㋛㋜㋝
|
392 | ㋞㋟㋠㋡㋢㋣㋤㋥㋦㋧㋨㋩㋪㋫㋬㋭㋮㋯㋰㋱㋲㋳㋴㋵㋶㋷㋸㋹㋺㋻㋼㋽
|
393 | ㋾㊊㊋㊌㊍㊎㊏㊐㊑㊒㊓㊔㊕㊖㊗㊘㊙㊚㊛㊜㊝㊞㊟㊠㊡㊢㊣㊤㊥㊦㊧㊨
|
394 | ㊩㊪㊫㊬㊭㊮㊯㊰㊀㊁㊂㊃㊄㊅㊆㊇㊈㊉㉈㉉㉊㉋㉌㉍㉎㉏⓵⓶⓷⓸⓹〓
|
395 | """
|
396 |
|
397 | aleph: """
|
398 | БДИЛЦЧШЭЮƆƋƏƐƔƥƧƸψŐőŒœŊŁłЯɔɘɐɕəɞ
|
399 | ␣!"#$%&'()*+,-./0123456789:;<=>?
|
400 | @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_
|
401 | `abcdefghijklmnopqrstuvwxyz{|}~ω
|
402 | ΓΔΘΛΞΠΣΦΨΩαβγδεζηθικλμνξπρςστυφχ
|
403 | Ж¡¢£¤¥¦§¨©ª«¬Я®¯°±²³´µ¶·¸¹º»¼½¾¿
|
404 | ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß
|
405 | àáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ
|
406 | """
|
407 |
|
408 | rdctn: """
|
409 | ∇≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡
|
410 | ␣!"#$%&'()*+,-./0123456789:;<=>?
|
411 | @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_
|
412 | `abcdefghijklmnopqrstuvwxyz{|}~≡
|
413 | ∃∃∃∃∃∃∃∃∃∃∃∃∃∃∃∃∃∃∃∃∃∃∃∃∃∃∃∃∃∃∃∃
|
414 | ∃∃¢£¤¥¦§¨©ª«¬Я®¯°±²³´µ¶·¸¹º»¼½¾¿
|
415 | ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß
|
416 | àáâãäåæçèéêëìíîïðñò≢≢≢≢≢≢≢≢≢≢≢≢Δ
|
417 | """
|
418 |
|
419 |
|
420 |
|
421 | @rpr_of_buffer = ( buffer, encoding = 'rdctn' ) ->
|
422 | return ( rpr buffer ) + ' ' + @_encode_buffer buffer, encoding
|
423 |
|
424 |
|
425 | @_encode_buffer = ( buffer, encoding = 'rdctn' ) ->
|
426 |
|
427 | encoding = @encodings[ encoding ] unless isa.list encoding
|
428 | return ( encoding[ buffer[ idx ] ] for idx in [ 0 ... buffer.length ] ).join ''
|
429 |
|
430 |
|
431 | @_compile_encodings = ->
|
432 |
|
433 | chrs_of = ( text ) ->
|
434 | text = text.split /([\ud800-\udbff].|.)/
|
435 | return ( chr for chr in text when chr isnt '' )
|
436 |
|
437 | for name, encoding of @encodings
|
438 | encoding = chrs_of encoding.replace /\n+/g, ''
|
439 | unless ( length = encoding.length ) is 256
|
440 | throw new Error "µ69198 expected 256 characters, found #{length} in encoding #{rpr name}"
|
441 | @encodings[ name ] = encoding
|
442 | return null
|
443 | @_compile_encodings()
|
444 |
|
445 |
|
446 | @as_sortline = ( key, settings ) ->
|
447 | joiner = settings?[ 'joiner' ] ? ' '
|
448 | base = settings?[ 'base' ] ? 0x2800
|
449 | stringify = settings?[ 'stringify' ] ? JSON.stringify
|
450 | bare = settings?[ 'bare' ] ? no
|
451 | buffer = @encode key
|
452 | buffer_txt = ( String.fromCodePoint base + buffer[ idx ] for idx in [ 0 ... buffer.length - 1 ] ).join ''
|
453 | return buffer_txt if bare
|
454 | return buffer_txt + joiner + stringify key
|
455 |
|
456 |
|
457 |
|
458 |
|
459 |
|