aboutsummaryrefslogtreecommitdiff
path: root/stdlib/source/library/lux/data/binary.lux
blob: e4a07b78834075bd8dac5c5a231f4fa47f08ca7a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
(.module:
  [library
   [lux {"-" [i64]}
    ["@" target]
    ["[0]" ffi]
    [abstract
     [monad {"+" [do]}]
     [equivalence {"+" [Equivalence]}]
     [monoid {"+" [Monoid]}]]
    [control
     ["[0]" maybe]
     ["[0]" try {"+" [Try]}]
     ["[0]" exception {"+" [exception:]}]]
    [data
     [text
      ["%" format {"+" [format]}]]
     [collection
      ["[0]" array]]]
    [math
     [number {"+" [hex]}
      ["n" nat]
      ["f" frac]
      ["[0]" i64]]]]])

(exception: .public (index_out_of_bounds [size Nat
                                          index Nat])
  (exception.report
   ["Size" (%.nat size)]
   ["Index" (%.nat index)]))

(exception: .public (slice_out_of_bounds [size Nat
                                          offset Nat
                                          length Nat])
  (exception.report
   ["Size" (%.nat size)]
   ["Offset" (%.nat offset)]
   ["Length" (%.nat length)]))

(with_expansions [<jvm> (as_is (type: .public Binary
                                 (ffi.type [byte]))

                               (ffi.import: java/lang/Object)
                               
                               (ffi.import: java/lang/System
                                 ["[1]::[0]"
                                  ("static" arraycopy [java/lang/Object int java/lang/Object int int] "try" void)])

                               (ffi.import: java/util/Arrays
                                 ["[1]::[0]"
                                  ("static" copyOfRange [[byte] int int] [byte])
                                  ("static" equals [[byte] [byte]] boolean)])

                               (def: byte_mask
                                 I64
                                 (|> i64.bits_per_byte i64.mask .i64))

                               (def: i64
                                 (-> (primitive "java.lang.Byte") I64)
                                 (|>> ffi.byte_to_long (:as I64) (i64.and ..byte_mask)))

                               (def: byte
                                 (-> (I64 Any) (primitive "java.lang.Byte"))
                                 (for [@.old
                                       (|>> .int ffi.long_to_byte)

                                       @.jvm
                                       (|>> .int (:as (primitive "java.lang.Long")) ffi.long_to_byte)])))]
  (for [@.old (as_is <jvm>)
        @.jvm (as_is <jvm>)

        @.js
        (as_is (ffi.import: ArrayBuffer
                 ["[1]::[0]"
                  (new [ffi.Number])])
               
               (ffi.import: Uint8Array
                 ["[1]::[0]"
                  (new [ArrayBuffer])
                  (length ffi.Number)])
               
               (type: .public Binary
                 Uint8Array))

        @.python
        (type: .public Binary
          (primitive "bytearray"))

        @.scheme
        (as_is (type: .public Binary
                 (primitive "bytevector"))
               
               (ffi.import: (make-bytevector [Nat] Binary))
               (ffi.import: (bytevector-u8-ref [Binary Nat] I64))
               (ffi.import: (bytevector-u8-set! [Binary Nat (I64 Any)] Any))
               (ffi.import: (bytevector-length [Binary] Nat)))]

       ... Default
       (type: .public Binary
         (array.Array (I64 Any)))))

(template: (!size binary)
  [(for [@.old (ffi.length binary)
         @.jvm (ffi.length binary)

         @.js
         (|> binary
             Uint8Array::length
             f.nat)

         @.python
         (|> binary
             (:as (array.Array (I64 Any)))
             "python array length")

         @.scheme
         (..bytevector-length [binary])]

        ... Default
        (array.size binary))])

(template: (!read index binary)
  [(for [@.old (..i64 (ffi.read! index binary))
         @.jvm (..i64 (ffi.read! index binary))

         @.js
         (|> binary
             (: ..Binary)
             (:as (array.Array .Frac))
             ("js array read" index)
             f.nat
             .i64)

         @.python
         (|> binary
             (:as (array.Array .I64))
             ("python array read" index))

         @.scheme
         (..bytevector-u8-ref [binary index])]

        ... Default
        (|> binary
            (array.read! index)
            (maybe.else (: (I64 Any) 0))
            (:as I64)))])

(template: (!!write <byte_type> <post> <write> index value binary)
  [(|> binary
       (: ..Binary)
       (:as (array.Array <byte_type>))
       (<write> index (|> value .nat (n.% (hex "100")) <post>))
       (:as ..Binary))])

(template: (!write index value binary)
  [(for [@.old (ffi.write! index (..byte value) binary)
         @.jvm (ffi.write! index (..byte value) binary)

         @.js (!!write .Frac n.frac "js array write" index value binary)
         @.python (!!write (I64 Any) (:as (I64 Any)) "python array write" index value binary)
         @.scheme (exec (..bytevector-u8-set! [binary index value])
                    binary)]

        ... Default
        (array.write! index (|> value .nat (n.% (hex "100"))) binary))])

(def: .public size
  (-> Binary Nat)
  (|>> !size))

(def: .public (empty size)
  (-> Nat Binary)
  (for [@.old (ffi.array byte size)
        @.jvm (ffi.array byte size)

        @.js
        (|> size n.frac ArrayBuffer::new Uint8Array::new)

        @.python
        (|> size
            ("python apply" (:as ffi.Function ("python constant" "bytearray")))
            (:as Binary))

        @.scheme
        (..make-bytevector size)]

       ... Default
       (array.empty size)))

(def: .public (aggregate f init binary)
  (All (_ a) (-> (-> I64 a a) a Binary a))
  (let [size (..!size binary)]
    (loop [index 0
           output init]
      (if (n.< size index)
        (recur (++ index) (f (!read index binary) output))
        output))))

(def: .public (read/8! index binary)
  (-> Nat Binary (Try I64))
  (if (n.< (..!size binary) index)
    {#try.Success (!read index binary)}
    (exception.except ..index_out_of_bounds [(..!size binary) index])))

(def: .public (read/16! index binary)
  (-> Nat Binary (Try I64))
  (if (n.< (..!size binary) (n.+ 1 index))
    {#try.Success ($_ i64.or
                      (i64.left_shifted 8 (!read index binary))
                      (!read (n.+ 1 index) binary))}
    (exception.except ..index_out_of_bounds [(..!size binary) index])))

(def: .public (read/32! index binary)
  (-> Nat Binary (Try I64))
  (if (n.< (..!size binary) (n.+ 3 index))
    {#try.Success ($_ i64.or
                      (i64.left_shifted 24 (!read index binary))
                      (i64.left_shifted 16 (!read (n.+ 1 index) binary))
                      (i64.left_shifted 8 (!read (n.+ 2 index) binary))
                      (!read (n.+ 3 index) binary))}
    (exception.except ..index_out_of_bounds [(..!size binary) index])))

(def: .public (read/64! index binary)
  (-> Nat Binary (Try I64))
  (if (n.< (..!size binary) (n.+ 7 index))
    {#try.Success ($_ i64.or
                      (i64.left_shifted 56 (!read index binary))
                      (i64.left_shifted 48 (!read (n.+ 1 index) binary))
                      (i64.left_shifted 40 (!read (n.+ 2 index) binary))
                      (i64.left_shifted 32 (!read (n.+ 3 index) binary))
                      (i64.left_shifted 24 (!read (n.+ 4 index) binary))
                      (i64.left_shifted 16 (!read (n.+ 5 index) binary))
                      (i64.left_shifted 8 (!read (n.+ 6 index) binary))
                      (!read (n.+ 7 index) binary))}
    (exception.except ..index_out_of_bounds [(..!size binary) index])))

(def: .public (write/8! index value binary)
  (-> Nat (I64 Any) Binary (Try Binary))
  (if (n.< (..!size binary) index)
    {#try.Success (|> binary
                      (!write index value))}
    (exception.except ..index_out_of_bounds [(..!size binary) index])))

(def: .public (write/16! index value binary)
  (-> Nat (I64 Any) Binary (Try Binary))
  (if (n.< (..!size binary) (n.+ 1 index))
    {#try.Success (|> binary
                      (!write index (i64.right_shifted 8 value))
                      (!write (n.+ 1 index) value))}
    (exception.except ..index_out_of_bounds [(..!size binary) index])))

(def: .public (write/32! index value binary)
  (-> Nat (I64 Any) Binary (Try Binary))
  (if (n.< (..!size binary) (n.+ 3 index))
    {#try.Success (|> binary
                      (!write index (i64.right_shifted 24 value))
                      (!write (n.+ 1 index) (i64.right_shifted 16 value))
                      (!write (n.+ 2 index) (i64.right_shifted 8 value))
                      (!write (n.+ 3 index) value))}
    (exception.except ..index_out_of_bounds [(..!size binary) index])))

(def: .public (write/64! index value binary)
  (-> Nat (I64 Any) Binary (Try Binary))
  (if (n.< (..!size binary) (n.+ 7 index))
    (for [@.scheme (let [write_high (|>> (!write index (i64.right_shifted 56 value))
                                         (!write (n.+ 1 index) (i64.right_shifted 48 value))
                                         (!write (n.+ 2 index) (i64.right_shifted 40 value))
                                         (!write (n.+ 3 index) (i64.right_shifted 32 value)))
                         write_low (|>> (!write (n.+ 4 index) (i64.right_shifted 24 value))
                                        (!write (n.+ 5 index) (i64.right_shifted 16 value))
                                        (!write (n.+ 6 index) (i64.right_shifted 8 value))
                                        (!write (n.+ 7 index) value))]
                     (|> binary write_high write_low #try.Success))]
         {#try.Success (|> binary
                           (!write index (i64.right_shifted 56 value))
                           (!write (n.+ 1 index) (i64.right_shifted 48 value))
                           (!write (n.+ 2 index) (i64.right_shifted 40 value))
                           (!write (n.+ 3 index) (i64.right_shifted 32 value))
                           (!write (n.+ 4 index) (i64.right_shifted 24 value))
                           (!write (n.+ 5 index) (i64.right_shifted 16 value))
                           (!write (n.+ 6 index) (i64.right_shifted 8 value))
                           (!write (n.+ 7 index) value))})
    (exception.except ..index_out_of_bounds [(..!size binary) index])))

(implementation: .public equivalence
  (Equivalence Binary)
  
  (def: (= reference sample)
    (with_expansions [<jvm> (java/util/Arrays::equals reference sample)]
      (for [@.old <jvm>
            @.jvm <jvm>]
           (let [limit (!size reference)]
             (and (n.= limit
                       (!size sample))
                  (loop [index 0]
                    (if (n.< limit index)
                      (and (n.= (!read index reference)
                                (!read index sample))
                           (recur (++ index)))
                      true))))))))

(for [@.old (as_is)
      @.jvm (as_is)]

     ... Default
     (exception: .public (cannot_copy_bytes [bytes Nat
                                             source_input Nat
                                             target_output Nat])
       (exception.report
        ["Bytes" (%.nat bytes)]
        ["Source input space" (%.nat source_input)]
        ["Target output space" (%.nat target_output)])))

(def: .public (copy bytes source_offset source target_offset target)
  (-> Nat Nat Binary Nat Binary (Try Binary))
  (with_expansions [<jvm> (as_is (do try.monad
                                   [_ (java/lang/System::arraycopy source (.int source_offset) target (.int target_offset) (.int bytes))]
                                   (in target)))]
    (for [@.old <jvm>
          @.jvm <jvm>]
         
         ... Default
         (let [source_input (n.- source_offset (!size source))
               target_output (n.- target_offset (!size target))]
           (if (n.> source_input bytes)
             (exception.except ..cannot_copy_bytes [bytes source_input target_output])
             (loop [index 0]
               (if (n.< bytes index)
                 (exec (!write (n.+ target_offset index)
                               (!read (n.+ source_offset index) source)
                               target)
                   (recur (++ index)))
                 {#try.Success target})))))))

(def: .public (slice offset length binary)
  (-> Nat Nat Binary (Try Binary))
  (let [size (..!size binary)
        limit (n.+ length offset)]
    (if (n.> size limit)
      (exception.except ..slice_out_of_bounds [size offset length])
      (with_expansions [<jvm> (as_is {#try.Success (java/util/Arrays::copyOfRange binary (.int offset) (.int limit))})]
        (for [@.old <jvm>
              @.jvm <jvm>]
             
             ... Default
             (..copy length offset binary 0 (..empty length)))))))

(def: .public (after bytes binary)
  (-> Nat Binary Binary)
  (case bytes
    0 binary
    _ (let [distance (n.- bytes (..!size binary))]
        (case (..slice bytes distance binary)
          {#try.Success slice}
          slice
          
          {#try.Failure _}
          (..empty 0)))))

(implementation: .public monoid
  (Monoid Binary)

  (def: identity
    (..empty 0))

  (def: (composite left right)
    (let [sizeL (!size left)
          sizeR (!size right)
          output (..empty (n.+ sizeL sizeR))]
      (exec
        (..copy sizeL 0 left 0 output)
        (..copy sizeR 0 right sizeL output)
        output))))