1(*
2    Title:      Standard Basis Library: Word8Array, Word8Vector and Byte Structures
3    Author:     David Matthews
4    Copyright   David Matthews 1999, 2005, 2015-16, 2018
5
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License version 2.1 as published by the Free Software Foundation.
9    
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14    
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18*)
19
20local
21    (* We can't use the segment length for the length of the vector
22       as we do for "normal" arrays and vectors.  There are two ways
23       of handling this.  We could  implement byte vectors in the same
24       way as strings, with a length word in the first word, or we
25       could store the length separately, as with arrays.  We could, of
26       course, treat arrays in the same way.  Implementing vectors as
27       strings simplifies conversion between the two and that's the
28       approach I've adopted. *)
29    open LibrarySupport
30
31    type vector = LibrarySupport.Word8Array.vector
32    datatype array = datatype LibrarySupport.Word8Array.array
33
34    val System_move_bytes: address*address*word*word*word->unit = RunCall.moveBytes
35
36    fun System_move_str(src: vector, dst: address, srcOffset: word, dstOffset: word, length: word): unit =
37        RunCall.moveBytes(src, RunCall.unsafeCast dst, srcOffset, dstOffset, length)
38
39    val emptyVec: vector = w8vectorFromString "" (* This is represented by a null string not a null vector. *)
40
41    val maxLen = CharVector.maxLen
42
43    val wVecLength: vector -> word = LibrarySupport.Word8Array.wVecLength
44    val vecLength: vector -> int = Word.toInt o wVecLength
45
46    (* Casts between int and word. *)
47    val intAsWord: int -> word = RunCall.unsafeCast
48    and wordAsInt: word -> int = RunCall.unsafeCast
49
50
51    infix 9 sub (* For what it's worth *)
52
53in
54    (* We don't use opaque matching because we need the internal representation of vector
55       and array in the IO structures. *)
56    structure Word8Vector : MONO_VECTOR =
57        struct
58        type elem = Word8.word
59        type vector = vector
60
61        val maxLen = maxLen;
62
63        val length = vecLength
64    
65        fun op sub (v, i: int): elem =
66            if i < 0 orelse i >= length v then raise General.Subscript
67            else RunCall.loadByteFromImmutable (v, intAsWord i + wordSize)
68     
69        (* Because Word8Vector.vector is implemented as a string and Word8.word
70           as a byte all these functions have the same implementation in
71           Word8Vector and CharVector.  We might be able to avoid the casts
72           by some clever use of opaque matching but we would have to do the
73           conversion of Word8.word from char to an opaque type at the same
74           time as converting Word8Vector.elem to preserve the sharing. *)
75        (* Can't that be achieved by Word8Vector :> MONO_VECTOR where type elem = Word8.word ? *)
76        val fromList: Word8.word list -> vector =
77            RunCall.unsafeCast CharVector.fromList
78        and tabulate: int * (int->Word8.word) -> vector =
79            RunCall.unsafeCast CharVector.tabulate
80        and concat: vector list -> vector = RunCall.unsafeCast CharVector.concat
81        and map: (elem -> elem) -> vector -> vector =
82            RunCall.unsafeCast CharVector.map
83        and mapi: ((int * elem) -> elem) -> vector -> vector =
84            RunCall.unsafeCast CharVector.mapi
85        and update: vector * int * elem -> vector =
86            RunCall.unsafeCast CharVector.update
87            
88        (* Create the other functions. *)
89        structure VectorOps =
90            VectorOperations(
91                struct
92                    type vector = vector and elem = elem
93                    val length = wVecLength
94                    fun unsafeSub (s, i) = RunCall.loadByteFromImmutable(s, i + wordSize)
95                    fun unsafeSet _ = raise Fail "Should not be called"
96                end);
97    
98        open VectorOps;
99
100        
101        local
102            (* Install the pretty printer for Word8Vector.vector *)
103            (* We may have to do this outside the structure if we
104               have opaque signature matching. *)
105            fun pretty(depth: FixedInt.int) _ (x: vector) =
106                let
107                    open PolyML
108                    val last = length x - 1
109                    fun put_elem (index, w, (l, d)) =
110                        if d = 0 then ([PrettyString "...]"], d+1)
111                        else if d < 0 then ([], d+1)
112                        else
113                        (
114                        PrettyString("0wx" ^ Word8.toString w) ::
115                            (if index <> last then PrettyString "," :: PrettyBreak(1, 0) :: l else l),
116                        d+1
117                        )
118                in
119                    PrettyBlock(3, false, [],
120                        PrettyString "fromList[" ::
121                        (if depth <= 0 then [PrettyString "...]"]
122                         else #1 (foldri put_elem ([PrettyString "]"], depth - FixedInt.fromInt last) x) )
123                   )
124                end
125        in
126            val () = PolyML.addPrettyPrinter pretty
127        end
128    
129    end (* Vector *);
130
131    structure Word8Array : MONO_ARRAY =
132    struct  
133
134        type array = array
135        type elem = Word8.word
136        type vector = vector
137        val maxLen = maxLen;
138    
139        fun length(Array(l, _)) = wordAsInt l
140        
141        fun array (length, ini) =
142        let
143            val len = unsignedShortOrRaiseSize length
144            val vec = LibrarySupport.allocBytes len
145            (* LibrarySupport.allocBytes does not initialise anything except the overflow bytes *)
146            fun init i = 
147                if len <= i then ()
148                else (RunCall.storeByte(vec, i, ini); init(i+0w1))
149        in
150            init 0w0;
151            Array(len, vec)
152        end
153    
154        fun op sub (Array(l, v), i: int): elem =
155            if i < 0 orelse i >= wordAsInt l then raise General.Subscript
156            else RunCall.loadByte (v, intAsWord i)
157    
158        fun update (Array (l, v), i: int, new) : unit =
159            if i < 0 orelse i >= wordAsInt l
160            then raise General.Subscript
161            else RunCall.storeByte (v, intAsWord i, new);
162    
163        (* Create an array from a list. *)
164        fun fromList (l : elem list) : array =
165        let
166            val length = unsignedShortOrRaiseSize(List.length l)
167                
168            (* Make an unitialised array. *)
169            val vec = LibrarySupport.allocBytes length;
170            
171            (* Copy the list elements into the array. *)
172            fun init (v, i, a :: l) = (RunCall.storeByte(v, i, a); init(v, i + 0w1, l))
173            |  init (_, _, []) = ();
174            
175        in
176            init(vec, 0w0, l);
177            Array(length, vec)
178        end
179            
180        fun tabulate (length: int , f : int->elem): array =
181        let
182            val len = unsignedShortOrRaiseSize length
183            val vec = LibrarySupport.allocBytes len
184            (* Initialise it to the function values. *)
185            fun init i = 
186                if len <= i then ()
187                else (RunCall.storeByte(vec, i, f(wordAsInt i)); init(i+0w1))
188        in
189            init 0w0;
190            Array(len, vec)
191        end
192        
193
194        fun vector(Array(len, vec)) =
195            if len = 0w0 then emptyVec
196            else
197            let
198                (* Make an array initialised to zero. *)
199                val new_vec = allocString len
200            in
201                System_move_bytes(vec, RunCall.unsafeCast new_vec, 0w0, wordSize, len);
202                RunCall.clearMutableBit new_vec;
203                w8vectorFromString new_vec
204            end
205    
206        (* Copy an array into another.  It's possible for the arrays to be the
207           same but in that case di must be zero (since len = dlen) and the copy is
208           a no-op. *)
209        fun copy {src=Array (len, s), dst=Array (dlen, d), di: int} =
210            let
211                val diW = unsignedShortOrRaiseSubscript di
212            in
213                if diW+len > dlen
214                then raise General.Subscript
215                else System_move_bytes(s, d, 0w0, diW, len)
216            end
217    
218        (* Copy a vector into an array. *)
219        fun copyVec {src, dst=Array (dlen, d), di: int} =
220            let
221                val len = intAsWord(vecLength src)
222                val diW = unsignedShortOrRaiseSubscript di
223            in
224                if diW + len > dlen
225                then raise General.Subscript
226               else System_move_str(src, d, wordSize, diW, len)
227            end
228
229        (* Create the other functions. *)
230        structure ArrayOps =
231            VectorOperations(
232                struct
233                    type vector = array and elem = elem
234                    fun length(Array(len, _)) = len
235                    fun unsafeSub(Array(_, v), i) = RunCall.loadByte(v, i)
236                    and unsafeSet(Array(_, v), i, c) = RunCall.storeByte(v, i, c)
237                end);
238    
239        open ArrayOps;
240    
241        local
242            (* Install the pretty printer for Word8Array.array *)
243            (* We may have to do this outside the structure if we
244               have opaque signature matching. *)
245            fun pretty(depth: FixedInt.int) _ (x: array) =
246                let
247                    open PolyML
248                    val last = length x - 1
249                    fun put_elem (index, w, (l, d)) =
250                        if d = 0 then ([PrettyString "...]"], d+1)
251                        else if d < 0 then ([], d+1)
252                        else
253                        (
254                        PrettyString("0wx" ^ Word8.toString w) ::
255                            (if index <> last then PrettyString "," :: PrettyBreak(1, 0) :: l else l),
256                        d+1
257                        )
258                in
259                    PrettyBlock(3, false, [],
260                        PrettyString "fromList[" ::
261                        (if depth <= 0 then [PrettyString "...]"]
262                         else #1 (foldri put_elem ([PrettyString "]"], depth - FixedInt.fromInt last) x) )
263                   )
264                end
265        in
266            val () = PolyML.addPrettyPrinter pretty
267        end
268    end (* Word8Array *);
269    
270    structure Word8VectorSlice:> MONO_VECTOR_SLICE where type elem = Word8.word where type vector = Word8Vector.vector =
271    (* We use opaque matching here simply to remove a confusing reference to VectorSliceOps when the
272       type is printed. *)
273    struct
274        type vector = vector and elem = Word8.word
275
276        structure VectorSliceOps =
277            VectorSliceOperations(
278                struct
279                    type vector = vector and elem = Word8.word
280                    val vecLength = wVecLength
281                    fun unsafeVecSub(s, i: word) = RunCall.loadByteFromImmutable(s, i + wordSize)
282                    fun unsafeVecUpdate _ = raise Fail "Should not be called" (* Not applicable *)
283                end);
284    
285        open VectorSliceOps;
286
287        (* vector: get the slice out.  Since the underlying vector is implemented using the basic
288           string type we can use substring here. *)
289        fun vector slice : vector =
290        let
291            val (vector, start, length) = base slice
292        in
293            (* It's possible to use an unsafe substring here if necessary since we've already
294               checked that the start and length are within the string. *)
295            w8vectorFromString(String.substring(w8vectorToString vector, start, length))
296        end;
297        
298        (* It would be more efficient to do these as single operations but it's probably too complicated. *)
299        fun concat L = Word8Vector.concat(List.map vector L)
300        fun map f slice = Word8Vector.map f (vector slice)
301        fun mapi f slice = Word8Vector.mapi f (vector slice)
302    
303    end (* Word8VectorSlice *);
304
305    local
306        (* Install the pretty printer for Word8VectorSlice.slice *)
307        (* We may have to do this outside the structure if we
308           have opaque signature matching. *)
309        fun pretty(depth: FixedInt.int) _ (x: Word8VectorSlice.slice) =
310            let
311                open PolyML Word8VectorSlice
312                val last = length x - 1
313                fun put_elem (index, w, (l, d)) =
314                    if d = 0 then ([PrettyString "...]"], d+1)
315                    else if d < 0 then ([], d+1)
316                    else
317                    (
318                    PrettyString("0wx" ^ Word8.toString w) ::
319                        (if index <> last then PrettyString "," :: PrettyBreak(1, 0) :: l else l),
320                    d+1
321                    )
322            in
323                PrettyBlock(3, false, [],
324                    PrettyString "fromList[" ::
325                    (if depth <= 0 then [PrettyString "...]"]
326                     else #1 (foldri put_elem ([PrettyString "]"], depth - FixedInt.fromInt last) x) )
327               )
328            end
329    in
330        val _ = PolyML.addPrettyPrinter pretty
331    end;
332
333    structure Word8ArraySlice:> MONO_ARRAY_SLICE where type elem = Word8.word where type vector = Word8Vector.vector
334                    where type vector_slice = Word8VectorSlice.slice where type array = Word8Array.array =
335    struct
336        structure VectorSliceOps =
337            VectorSliceOperations(
338                struct
339                    type vector = array and elem = Word8.word
340                    fun unsafeVecSub(Array(_, s), i) = RunCall.loadByte(s, i)
341                    and unsafeVecUpdate(Array(_, s), i, x) = RunCall.storeByte (s, i, x)
342                    and vecLength(Array(l, _)) = l
343                end);
344    
345        open VectorSliceOps;
346
347        type elem = Word8.word
348        type vector = vector
349        type array = array
350        type vector_slice = Word8VectorSlice.slice
351
352        (* vector: get the slice out. *)
353        fun vector slice: vector =
354            let
355                val (Array(_, vec), start, length) = base slice
356            in
357                if length = 0 then emptyVec
358                else
359                let
360                    val len = intAsWord length
361                    (* Make an array initialised to zero. *)
362                    val new_vec = allocString len
363                in
364                    System_move_bytes(vec, RunCall.unsafeCast new_vec, intAsWord start, wordSize, len);
365                    RunCall.clearMutableBit new_vec;
366                    w8vectorFromString new_vec
367                end
368            end
369
370        (* Copy a slice into an array.  N.B. The arrays could be the same. *)
371        fun copy {src, dst, di: int} =
372        let
373            val (src, start, length) = base src
374        in
375            if di < 0 orelse di+length > Word8Array.length dst
376            then raise General.Subscript
377            else (* We can't use MoveBytes because of the potential overlap problem.
378                    Instead we use explicit copying choosing to copy up or down depending
379                    on the index whether the source and destination are the same or not.
380                    We could use MoveBytes if we know the arrays are different. *)
381            let
382                fun copyUp n =
383                if n = length then ()
384                else (Word8Array.update(dst, n+di, Word8Array.sub(src, n+start)); copyUp(n+1))
385                
386                and copyDown n =
387                if n < 0 then ()
388                else (Word8Array.update(dst, n+di, Word8Array.sub(src, n+start)); copyDown(n-1))
389            in
390                if di > start then copyDown(length-1) else copyUp 0
391            end
392        end
393    
394        (* Copy a vector slice into an array. *)
395        fun copyVec {src: Word8VectorSlice.slice, dst=Array (dlen, d), di: int} =
396            let
397                val (source, i, l) = Word8VectorSlice.base src
398                val len = intAsWord l and offset = intAsWord i
399                val diW = unsignedShortOrRaiseSubscript di
400            in
401                if diW + len > dlen
402                then raise General.Subscript
403                else System_move_str(source, d, offset + wordSize, diW, len)
404            end
405        
406    end (* Word8ArraySlice *);
407
408    local
409        (* Install the pretty printer for Word8ArraySlice.slice *)
410        (* We may have to do this outside the structure if we
411           have opaque signature matching. *)
412        fun pretty(depth: FixedInt.int) _ (x: Word8ArraySlice.slice) =
413            let
414                open PolyML Word8ArraySlice
415                val last = length x - 1
416                fun put_elem (index, w, (l, d)) =
417                    if d = 0 then ([PrettyString "...]"], d+1)
418                    else if d < 0 then ([], d+1)
419                    else
420                    (
421                    PrettyString("0wx" ^ Word8.toString w) ::
422                        (if index <> last then PrettyString "," :: PrettyBreak(1, 0) :: l else l),
423                    d+1
424                    )
425            in
426                PrettyBlock(3, false, [],
427                    PrettyString "fromList[" ::
428                    (if depth <= 0 then [PrettyString "...]"]
429                     else #1 (foldri put_elem ([PrettyString "]"], depth - FixedInt.fromInt last) x) )
430               )
431            end
432    in
433        val () = PolyML.addPrettyPrinter pretty
434    end
435
436end;
437