UNPKG

22.9 kBtext/coffeescriptView Raw
1
2
3
4############################################################################################################
5CND = require 'cnd'
6rpr = CND.rpr
7badge = 'HOLLERITH-CODEC/MAIN'
8debug = CND.get_logger 'debug', badge
9warn = 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
18VOID = Symbol 'VOID'
19
20
21#-----------------------------------------------------------------------------------------------------------
22@[ 'typemarkers' ] = {}
23#...........................................................................................................
24tm_lo = @[ 'typemarkers' ][ 'lo' ] = 0x00
25tm_null = @[ 'typemarkers' ][ 'null' ] = 'B'.codePointAt 0 # 0x42
26tm_false = @[ 'typemarkers' ][ 'false' ] = 'C'.codePointAt 0 # 0x43
27tm_true = @[ 'typemarkers' ][ 'true' ] = 'D'.codePointAt 0 # 0x44
28tm_list = @[ 'typemarkers' ][ 'list' ] = 'E'.codePointAt 0 # 0x45
29tm_date = @[ 'typemarkers' ][ 'date' ] = 'G'.codePointAt 0 # 0x47
30tm_ninfinity = @[ 'typemarkers' ][ 'ninfinity' ] = 'J'.codePointAt 0 # 0x4a
31tm_nnumber = @[ 'typemarkers' ][ 'nnumber' ] = 'K'.codePointAt 0 # 0x4b
32tm_void = @[ 'typemarkers' ][ 'void' ] = 'L'.codePointAt 0 # 0x4c
33tm_pnumber = @[ 'typemarkers' ][ 'pnumber' ] = 'M'.codePointAt 0 # 0x4d
34tm_pinfinity = @[ 'typemarkers' ][ 'pinfinity' ] = 'N'.codePointAt 0 # 0x4e
35tm_text = @[ 'typemarkers' ][ 'text' ] = 'T'.codePointAt 0 # 0x54
36tm_private = @[ 'typemarkers' ][ 'private' ] = 'Z'.codePointAt 0 # 0x5a
37tm_hi = @[ 'typemarkers' ][ 'hi' ] = 0xff
38
39#-----------------------------------------------------------------------------------------------------------
40@[ 'bytecounts' ] = {}
41bytecount_singular = @[ 'bytecounts' ][ 'singular' ] = 1
42bytecount_typemarker = @[ 'bytecounts' ][ 'typemarker' ] = 1
43bytecount_float = @[ 'bytecounts' ][ 'float' ] = 9
44bytecount_date = @[ 'bytecounts' ][ 'date' ] = bytecount_float + 1
45
46#-----------------------------------------------------------------------------------------------------------
47@[ 'sentinels' ] = {}
48#...........................................................................................................
49### http://www.merlyn.demon.co.uk/js-datex.htm ###
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' ] = {}
61symbol_fallback = @[ 'fallback' ] = Symbol 'fallback'
62
63
64#===========================================================================================================
65# RESULT BUFFER (RBUFFER)
66#-----------------------------------------------------------------------------------------------------------
67rbuffer_min_size = 1024
68rbuffer_max_size = 65536
69rbuffer = Buffer.alloc rbuffer_min_size
70
71#-----------------------------------------------------------------------------------------------------------
72grow_rbuffer = ->
73 factor = 2
74 new_size = Math.floor rbuffer.length * factor + 0.5
75 # warn "µ44542 growing rbuffer to #{new_size} bytes"
76 new_result_buffer = Buffer.alloc new_size
77 rbuffer.copy new_result_buffer
78 rbuffer = new_result_buffer
79 return null
80
81#-----------------------------------------------------------------------------------------------------------
82release_extraneous_rbuffer_bytes = ->
83 if rbuffer.length > rbuffer_max_size
84 # warn "µ44543 shrinking rbuffer to #{rbuffer_max_size} bytes"
85 rbuffer = Buffer.alloc rbuffer_max_size
86 return null
87
88
89#===========================================================================================================
90# VARIANTS
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 ### TAINT not strictly needed as we eliminate VOID prior to decoding ###
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# PRIVATES
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 ### Built-in private types ###
135 switch type
136 when '-set'
137 null # already dealt with in `write`
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 ### Built-in private types ###
161 switch type
162 when '-set'
163 ### TAINT wasting bytes because wrapped twice ###
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# NUMBERS
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# DATES
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# TEXTS
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 # urge '©J2d6R', buffer[ idx ], buffer[ idx ] is tm_text
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# LISTS
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 ### TAINT wasting bytes because wrapped too deep ###
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# PUBLIC API
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 ### TAINT code duplication ###
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 # key_rpr.push "#{@rpr_of_buffer element, key[ 2 ]}"
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 ### eliminate VOID prior to decoding ###
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# debug ( require './dump' ).@rpr_of_buffer null, buffer = @encode [ 'aaa', [], ]
378# debug '©tP5xQ', @decode buffer
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 ### TAINT use switch, emit error if `encoding` not list or known key ###
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