-
Notifications
You must be signed in to change notification settings - Fork 4
/
raw_input.cljs
155 lines (132 loc) · 5.16 KB
/
raw_input.cljs
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
(ns fress.impl.raw-input
(:require-macros [fress.macros :refer [<< >>>]])
(:require [fress.impl.adler32 :as adler]
[fress.impl.buffer :as buf]
[fress.util :as util :refer [isBigEndian log dbg]])
(:import [goog.math Long]))
(defprotocol IRawInput
(readRawByte [this])
(readRawInt8 [this])
(readRawInt16 [this])
(readRawInt24 [this])
(readRawInt32 [this])
(readRawInt40 [this])
(readRawInt48 [this])
(readRawInt64 [this])
(readRawFloat [this])
(readRawDouble [this])
(readFully [this length]
#_[this bytes offset length])
(getBytesRead [this])
(reset [this])
(close [this] "throw EOF on any further reads, even if room")
(validateChecksum [this]))
(def ^:dynamic *throw-on-unsafe?* true)
(def L_U8_MAX_VALUE (Long.fromNumber util/U8_MAX_VALUE))
(def L_U32_MAX_VALUE (Long.fromNumber util/U32_MAX_VALUE))
(defn ^Long readRawInt32L [this]
(let [a (.and (Long.fromNumber (readRawByte this)) L_U8_MAX_VALUE)
b (.and (Long.fromNumber (readRawByte this)) L_U8_MAX_VALUE)
c (.and (Long.fromNumber (readRawByte this)) L_U8_MAX_VALUE)
d (.and (Long.fromNumber (readRawByte this)) L_U8_MAX_VALUE)]
(-> (.shiftLeft a 24)
(.or (.shiftLeft b 16))
(.or (.shiftLeft c 8))
(.or d)
(.and L_U32_MAX_VALUE))))
(defn ^Long readRawInt40L [this]
(let [high (Long.fromNumber (readRawByte this))
low (readRawInt32L this)]
(.add (.shiftLeft high 32) low)))
(defn ^Long readRawInt48L [this]
(let [high (Long.fromNumber (readRawByte this))
low (readRawInt40L this)]
(.add (.shiftLeft high 40) low)))
(defn ^Long readRawInt64L [this] ;; return long on unsafe?
(let [a (readRawByte this)
b (readRawByte this)
c (readRawByte this)
d (readRawByte this)
e (readRawByte this)
f (readRawByte this)
g (readRawByte this)
h (readRawByte this)]
(when *throw-on-unsafe?*
(if (<= a 127)
(when (or (<= 32 b) (< 1561 (+ b c d e f g h)))
(throw (js/Error. (str "i64 exceeds js/Number.MAX_SAFE_INTEGER"))))
(when (or (< a 255) (< b 224) (zero? h) )
(throw (js/Error. (str "i64 exceeds js/Number.MIN_SAFE_INTEGER"))))))
(let [a (.and (Long.fromNumber a) L_U8_MAX_VALUE)
b (.and (Long.fromNumber b) L_U8_MAX_VALUE)
c (.and (Long.fromNumber c) L_U8_MAX_VALUE)
d (.and (Long.fromNumber d) L_U8_MAX_VALUE)
e (.and (Long.fromNumber e) L_U8_MAX_VALUE)
f (.and (Long.fromNumber f) L_U8_MAX_VALUE)
g (.and (Long.fromNumber g) L_U8_MAX_VALUE)
h (.and (Long.fromNumber h) L_U8_MAX_VALUE)]
(-> (.shiftLeft a 56)
(.or (.shiftLeft b 48))
(.or (.shiftLeft c 40))
(.or (.shiftLeft d 32))
(.or (.shiftLeft e 24))
(.or (.shiftLeft f 16))
(.or (.shiftLeft g 8))
(.or h)))))
(defrecord RawInput [in checksum]
IRawInput
(getBytesRead ^number [this] (buf/getBytesRead in))
(readRawByte [this]
(let [byte (buf/readUnsignedByte in)]
(when checksum (adler/update! checksum byte))
byte))
(readFully [this length] ;=> signed-byte-array
;; need to clamp somehow so we dont read past end of written
;; need arity to provides byte-array destination
(let [bytes (buf/readSignedBytes in length)]
(when checksum (adler/update! checksum bytes 0 length))
bytes))
(reset [this]
(buf/reset in)
(when checksum (adler/reset checksum)))
(close [this] (buf/close in))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(readRawInt8 ^number [this] (readRawByte this))
(readRawInt16 ^number [this]
(let [high (readRawByte this)
low (readRawByte this)]
(+ (<< high 8) low)))
(readRawInt24 ^number [this]
(+ (<< (readRawByte this) 16)
(<< (readRawByte this) 8)
(readRawByte this)))
(readRawInt32 ^number [this] (.toNumber (readRawInt32L this)))
(readRawInt40 ^number [this] (.toNumber (readRawInt40L this)))
(readRawInt48 ^number [this] (.toNumber (readRawInt48L this)))
(readRawInt64 ^number [this] (.toNumber (readRawInt64L this)))
(readRawFloat ^number [this]
(let [bytes (js/Int8Array. 4)]
(dotimes [i 4]
(let [i (if isBigEndian i (- 3 i))]
(aset bytes i (readRawByte this))))
(aget (js/Float32Array. (.-buffer bytes)) 0)))
(readRawDouble ^number [this]
(let [bytes (js/Int8Array. 8)]
(dotimes [i 8]
(let [i (if isBigEndian i (- 7 i))]
(aset bytes i (readRawByte this))))
(aget (js/Float64Array. (.-buffer bytes)) 0)))
(validateChecksum [this]
(if (nil? checksum)
(readRawInt32 this)
(let [calculatedChecksum @checksum
receivedChecksum (readRawInt32 this)]
(if (not= calculatedChecksum receivedChecksum)
(throw
(js/Error. "Invalid footer checksum, expected " calculatedChecksum" got " receivedChecksum)))))))
(defn raw-input
([in](raw-input in 0))
([in start-index](raw-input in start-index true))
([in ^number start-index ^boolean validateAdler]
(let [in (buf/readable-buffer in start-index)]
(RawInput. in (if validateAdler (adler/adler32))))))