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