|
1 |
| -;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz> |
| 1 | +;; Copyright (c) 2015-2018 Andrey Antukh <niwi@niwi.nz> |
2 | 2 | ;; All rights reserved.
|
3 | 3 | ;;
|
4 | 4 | ;; Redistribution and use in source and binary forms, with or without
|
|
23 | 23 | ;; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
24 | 24 |
|
25 | 25 | (ns octet.spec.reference
|
| 26 | + "Spec types for arbitrary length byte arrays/strings with a length reference." |
26 | 27 | (:require [octet.buffer :as buffer]
|
27 | 28 | [octet.spec :as spec]
|
| 29 | + [octet.spec.basic :as basic-spec] |
28 | 30 | [octet.spec.string :as string-spec]))
|
29 | 31 |
|
30 |
| -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
31 |
| -;; Spec types for arbitrary length byte arrays/strings with a length reference |
32 |
| -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
33 | 32 |
|
34 |
| -(defn- ref-size [type data] |
35 |
| - (if (satisfies? spec/ISpecSize type) |
36 |
| - (.size type) |
37 |
| - (.size* type data))) |
| 33 | +(defn- ref-size |
| 34 | + [type data] |
| 35 | + (cond |
| 36 | + (satisfies? spec/ISpecDynamicSize type) |
| 37 | + (spec/size* type data) |
38 | 38 |
|
39 |
| -(defn- coerce-types [types] |
| 39 | + (satisfies? spec/ISpecSize type) |
| 40 | + (spec/size type) |
| 41 | + |
| 42 | + :else |
| 43 | + (throw (ex-info "Unexpected type" {:type type})))) |
| 44 | + |
| 45 | +(defn- coerce-types |
40 | 46 | "to make it terser to handle both maps (assoc spec) and seq/vector
|
41 | 47 | (indexed spec), we coerce the seq/vector types to maps where the
|
42 | 48 | keys are indexes"
|
43 |
| - (cond (map? types) types |
44 |
| - (or (seq? types) |
45 |
| - (vector? types)) (apply array-map (interleave (range) types)) |
46 |
| - :else (throw (ex-info "invalid type structure, not map, seq or vector" |
47 |
| - {:type-structure types})))) |
48 |
| - |
49 |
| -(defn- ref-len-offset [ref-kw-or-index types data] |
50 |
| - "for ref-xxxx* specs, calculate the byte offset of the |
| 49 | + [types] |
| 50 | + (cond |
| 51 | + (map? types) |
| 52 | + types |
| 53 | + |
| 54 | + (or (seq? types) |
| 55 | + (vector? types)) |
| 56 | + (apply array-map (interleave (range) types)) |
| 57 | + |
| 58 | + :else (throw (ex-info "invalid type structure, not map, seq or vector" |
| 59 | + {:type-structure types})))) |
| 60 | + |
| 61 | +(defn- ref-len-offset |
| 62 | + "for ref-xxxx specs, calculate the byte offset of the |
51 | 63 | spec containing the length, i.e. (spec :a (int16) :b (int16) c: (ref-string* :b))
|
52 | 64 | would cause this method to be called with :b (since the ref-string has a :b
|
53 | 65 | reference as its length) and should then return 2 as the second int16 is at byte offset 2"
|
| 66 | + [ref-kw-or-index types data] |
54 | 67 | (reduce-kv
|
55 |
| - (fn [acc kw-or-index type] |
56 |
| - (if (= ref-kw-or-index kw-or-index) |
57 |
| - (reduced acc) |
58 |
| - (+ acc (ref-size type (get data kw-or-index))))) |
59 |
| - 0 |
60 |
| - types)) |
61 |
| - |
62 |
| -(defn- ref-write* [ref-kw-or-index buff pos value types data] |
| 68 | + (fn [acc kw-or-index type] |
| 69 | + (if (= ref-kw-or-index kw-or-index) |
| 70 | + (reduced acc) |
| 71 | + (+ acc (ref-size type (get data kw-or-index))))) |
| 72 | + 0 |
| 73 | + types)) |
| 74 | + |
| 75 | +(defn- ref-write-length |
| 76 | + [type buff offset length] |
| 77 | + (cond |
| 78 | + (identical? basic-spec/int16 type) |
| 79 | + (buffer/write-short buff offset length) |
| 80 | + |
| 81 | + (identical? basic-spec/int32 type) |
| 82 | + (buffer/write-int buff offset length) |
| 83 | + |
| 84 | + (identical? basic-spec/int64 type) |
| 85 | + (buffer/write-long buff offset length) |
| 86 | + |
| 87 | + (identical? basic-spec/int64 type) |
| 88 | + (buffer/write-long buff offset length) |
| 89 | + |
| 90 | + (identical? basic-spec/uint16 type) |
| 91 | + (buffer/write-ushort buff offset length) |
| 92 | + |
| 93 | + (identical? basic-spec/uint32 type) |
| 94 | + (buffer/write-uint buff offset length) |
| 95 | + |
| 96 | + (identical? basic-spec/uint64 type) |
| 97 | + (buffer/write-ulong buff offset length) |
| 98 | + |
| 99 | + :else |
| 100 | + (throw (ex-info "Invalid reference type: should be int16, int32, int64 and unsigned variants" {})))) |
| 101 | + |
| 102 | +(defn- ref-write |
63 | 103 | "write a ref spec, will also write the length to the length spec"
|
| 104 | + [ref-kw-or-index buff pos value types data] |
64 | 105 | (let [input (if (string? value) (string-spec/string->bytes value) value)
|
65 | 106 | length (count input)
|
66 | 107 | types (coerce-types types)
|
67 | 108 | len-offset (ref-len-offset ref-kw-or-index types data)
|
68 | 109 | len-type (get types ref-kw-or-index)]
|
69 |
| - (.write len-type buff len-offset length) |
| 110 | + (ref-write-length len-type buff len-offset length) |
70 | 111 | (buffer/write-bytes buff pos length input)
|
71 | 112 | (+ length)))
|
72 | 113 |
|
73 |
| -(defn- ref-read* [ref-kw-or-index buff pos parent] |
| 114 | +(defn- ref-read |
74 | 115 | "read ref spec, will read the length from the length spec"
|
75 |
| - (let [datasize (cond (map? parent) (ref-kw-or-index parent) |
76 |
| - (or (seq? parent) (vector? parent)) (get parent ref-kw-or-index) |
77 |
| - :else (throw (ex-info |
78 |
| - (str "bad ref-string*/ref-bytes* length reference - " ref-kw-or-index) |
79 |
| - {:length-kw ref-kw-or-index |
80 |
| - :data-read parent}))) |
| 116 | + [ref-kw-or-index buff pos parent] |
| 117 | + (let [datasize (cond |
| 118 | + (map? parent) |
| 119 | + (ref-kw-or-index parent) |
| 120 | + |
| 121 | + (or (seq? parent) (vector? parent)) |
| 122 | + (get parent ref-kw-or-index) |
| 123 | + |
| 124 | + :else |
| 125 | + (throw (ex-info |
| 126 | + (str "bad ref-string/ref-bytes length reference - " ref-kw-or-index) |
| 127 | + {:length-kw ref-kw-or-index |
| 128 | + :data-read parent}))) |
| 129 | + ;; _ (println "FOFOFO:" parent ref-kw-or-index) |
81 | 130 | data (buffer/read-bytes buff pos datasize)]
|
82 | 131 | [datasize data]))
|
83 | 132 |
|
84 |
| -(defn ref-bytes* |
85 |
| - [ref-kw-or-index] |
| 133 | +(defn ref-bytes |
86 | 134 | "create a dynamic length byte array where the length of the byte array is stored in another
|
87 | 135 | spec within the containing indexed spec or associative spec. Example usages:
|
88 | 136 | (spec (int16) (int16) (ref-bytes* 1))
|
89 | 137 | (spec :a (int16) :b (int32) (ref-bytes* :b))
|
90 | 138 | where the first example would store the length of the byte array in the second int16 and
|
91 | 139 | the second example would store the length of the byte array in the int32 at key :b."
|
| 140 | + [ref-kw-or-index] |
92 | 141 | (reify
|
93 | 142 | #?@(:clj
|
94 | 143 | [clojure.lang.IFn
|
|
103 | 152 |
|
104 | 153 | spec/ISpecWithRef
|
105 | 154 | (read* [_ buff pos parent]
|
106 |
| - (ref-read* ref-kw-or-index buff pos parent)) |
| 155 | + (ref-read ref-kw-or-index buff pos parent)) |
107 | 156 |
|
108 | 157 | (write* [_ buff pos value types data]
|
109 |
| - (ref-write* ref-kw-or-index buff pos value types data)))) |
| 158 | + (ref-write ref-kw-or-index buff pos value types data)))) |
110 | 159 |
|
111 |
| -(defn ref-string* |
| 160 | +(defn ref-string |
112 | 161 | "create a dynamic length string where the length of the string is stored in another
|
113 | 162 | spec within the containing indexed spec or associative spec. Example usages:
|
114 | 163 | (spec (int16) (int16) (ref-string* 1))
|
|
131 | 180 |
|
132 | 181 | spec/ISpecWithRef
|
133 | 182 | (read* [_ buff pos parent]
|
134 |
| - (let [[datasize bytes] (ref-read* ref-kw-or-index buff pos parent)] |
| 183 | + (let [[datasize bytes] (ref-read ref-kw-or-index buff pos parent)] |
135 | 184 | [datasize (string-spec/bytes->string bytes datasize)]))
|
136 | 185 |
|
137 | 186 | (write* [_ buff pos value types data]
|
138 |
| - (ref-write* ref-kw-or-index buff pos value types data)))) |
139 |
| - |
140 |
| -(defn ref-vector* |
141 |
| - [ref-kw-or-index] |
142 |
| - ;; TODO: implement this |
143 |
| - ) |
| 187 | + (ref-write ref-kw-or-index buff pos value types data)))) |
0 commit comments