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 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
|
(*
Title: Standard Basis Library: IO Support functions
Copyright David C.J. Matthews 2000, 2015-16
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 2.1 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*)
(* This function provides wrappers for the RTS file descriptors to construct
TextPrimIO and BinPrimIO readers and writers. It is used both from the
TextIO and BinIO structures and also from the Windows and Unix structures
to wrap up pipes. *)
structure LibraryIOSupport:
sig
val wrapInFileDescr :
{ fd : OS.IO.iodesc, name : string, initBlkMode : bool } -> TextPrimIO.reader
val wrapOutFileDescr :
{ fd : OS.IO.iodesc, name : string, appendMode : bool,
initBlkMode : bool, chunkSize : int } -> TextPrimIO.writer
val wrapBinInFileDescr :
{ fd : OS.IO.iodesc, name : string, initBlkMode : bool } -> BinPrimIO.reader
val wrapBinOutFileDescr :
{ fd : OS.IO.iodesc, name : string, appendMode : bool,
initBlkMode : bool, chunkSize : int } -> BinPrimIO.writer
val readBinVector: OS.IO.iodesc * int -> Word8Vector.vector
val readBinArray: OS.IO.iodesc * Word8ArraySlice.slice -> int
val writeBinVec: OS.IO.iodesc * Word8VectorSlice.slice -> int
val writeBinArray: OS.IO.iodesc * Word8ArraySlice.slice -> int
val nonBlocking : ('a->'b) -> 'a ->'b option
val protect: Thread.Mutex.mutex -> ('a -> 'b) -> 'a -> 'b
end
=
struct
(* open IO *)
type address = LibrarySupport.address
type fileDescr = OS.IO.iodesc
(* Called after any exception in the lower level reader or
writer to map any exception other than Io into Io. *)
local
val doIo = RunCall.rtsCallFull3 "PolyBasicIOGeneral"
in
fun sys_close (strm: fileDescr): unit = doIo(7, strm, 0)
and sys_block_in(strm: fileDescr): unit = doIo(27, strm, 0)
and sys_block_out(strm: fileDescr): unit = doIo(29, strm, 0)
end
local
val doIo = RunCall.rtsCallFull3 "PolyBasicIOGeneral"
in
fun sys_read_text (strm: fileDescr, vil: address*word*word): int =
doIo(8, strm, vil)
fun sys_write_text (strm: fileDescr, vil: address*word*word): int =
doIo(11, strm, vil)
fun sys_read_bin (strm: fileDescr, vil: address*word*word): int =
doIo(9, strm, vil)
fun sys_write_bin (strm: fileDescr, vil: address*word*word): int =
doIo(12, strm, vil)
end
local
val doIo = RunCall.rtsCallFull3 "PolyBasicIOGeneral"
in
fun sys_read_string (strm: fileDescr, len: int): string =
doIo(10, strm, len)
end
local
val doIo = RunCall.rtsCallFull3 "PolyBasicIOGeneral"
in
fun readBinVector (strm: fileDescr, len: int): Word8Vector.vector =
doIo(26, strm, len)
end
local
val doIo = RunCall.rtsCallFull3 "PolyBasicIOGeneral"
in
fun sys_get_buffsize (strm: fileDescr): int = doIo(15, strm, 0)
and sys_can_input(strm: fileDescr): int = doIo(16, strm, 0)
and sys_can_output(strm: fileDescr): int = doIo(28, strm, 0)
and sys_avail(strm: fileDescr): int = doIo(17, strm, 0)
and sys_get_iodesc(strm: fileDescr): int = doIo(30, strm, 0)
end
local
val doIo = RunCall.rtsCallFull3 "PolyBasicIOGeneral"
in
fun sys_get_pos(strm: fileDescr): Position.int = doIo(18, strm, 0) (* N.B. large int *)
end
local
val doIo = RunCall.rtsCallFull3 "PolyBasicIOGeneral"
in
fun sys_end_pos(strm: fileDescr): Position.int = doIo(20, strm, 0) (* N.B. large int *)
end
local
val doIo = RunCall.rtsCallFull3 "PolyBasicIOGeneral"
in
fun sys_set_pos(strm: fileDescr, p: Position.int): unit =
(doIo(19, strm, p); ()) (* N.B. large int *)
end
local
(* Find out the error which will be generated if a stream in
non-blocking mode would block. *)
val eAgain = OS.syserror "EAGAIN" and eWouldBlock = OS.syserror "EWOULDBLOCK"
and eInProgress = OS.syserror "EINPROGRESS"
and wsaWouldBlock = OS.syserror "WSAEWOULDBLOCK" and wsaInProgress = OS.syserror "WSAEINPROGRESS"
in
(* If evaluating the function raises EAGAIN or EWOULDBLOCK we return NONE
otherwise if it succeeds return SOME result. Pass other exceptions back
to the caller. *)
fun nonBlocking f arg =
SOME(f arg) handle exn as OS.SysErr(_, SOME e) =>
if (case eAgain of SOME again => e = again | NONE => false) then NONE
else if (case eWouldBlock of SOME wouldBlock => e = wouldBlock | NONE => false) then NONE
else if (case eInProgress of SOME inProgress => e = inProgress | NONE => false) then NONE
else if (case wsaWouldBlock of SOME wouldBlock => e = wouldBlock | NONE => false) then NONE
else if (case wsaInProgress of SOME inProgress => e = inProgress | NONE => false) then NONE
else raise exn
end
val wordSize : word = LibrarySupport.wordSize;
(* Find out if random access is permitted and return the
appropriate values. *)
fun getRandAccessFns n =
let
val isRandomAccess =
((sys_get_pos n; true) handle OS.SysErr _ => false)
val getPos =
if isRandomAccess
then SOME(fn () => sys_get_pos n)
else NONE
val setPos =
if isRandomAccess
then SOME(fn p => sys_set_pos(n, p))
else NONE
val endPos =
if isRandomAccess
then SOME(fn () => sys_end_pos n)
else NONE
in
(getPos, setPos, endPos)
end
fun writeBinArray (n: fileDescr, slice: Word8ArraySlice.slice): int =
let
val (buf, i, len) = Word8ArraySlice.base slice
val LibrarySupport.Word8Array.Array(_, v) = buf
val iW = LibrarySupport.unsignedShortOrRaiseSubscript i
val lenW = LibrarySupport.unsignedShortOrRaiseSubscript len
in
sys_write_bin(n, (v, iW, lenW))
end
fun readBinArray (n: fileDescr, slice: Word8ArraySlice.slice): int =
let
val (buf, i, len) = Word8ArraySlice.base slice
val LibrarySupport.Word8Array.Array(_, v) = buf
val lenW = LibrarySupport.unsignedShortOrRaiseSubscript len
val iW = LibrarySupport.unsignedShortOrRaiseSubscript i
in
sys_read_bin(n, (v, iW, lenW))
end
(* Write out a string using the underlying call. Note
that we have to add the size of a word to the offsets
to skip the length word. The underlying call deals
with the special case of a single character string
where the "string" is actually the character itself. *)
fun writeBinVec (n: fileDescr, slice: Word8VectorSlice.slice): int =
let
val (buf, i, len) = Word8VectorSlice.base slice
val iW = LibrarySupport.unsignedShortOrRaiseSubscript i
val lenW = LibrarySupport.unsignedShortOrRaiseSubscript len
in
sys_write_bin(n, (LibrarySupport.w8vectorAsAddress buf, iW+wordSize, lenW))
end
(* Create the primitive IO functions and add the higher layers.
For all file descriptors other than standard input we look
at the stream to see if we can do non-blocking input and/or
random access. Standard input, though is persistent and so
we have to take a more restrictive view. *)
fun wrapInFileDescr{ fd, name, initBlkMode } =
let
fun readArray (slice: CharArraySlice.slice): int =
let
val (buf, i, len) = CharArraySlice.base slice
val LibrarySupport.CharArray.Array(_, v) = buf
val iW = LibrarySupport.unsignedShortOrRaiseSubscript i
val lenW = LibrarySupport.unsignedShortOrRaiseSubscript len
in
sys_read_text(fd, (v, iW, lenW))
end
fun readVector l = sys_read_string(fd, l)
(* If we have opened the stream in non-blocking mode readVec
and readArray will raise an exception if they would block.
We have to handle that. The blocking functions can be
constructed using block_in but that should be done by
augmentReader. *)
val (readVec, readArr, readVecNB, readArrNB) =
if initBlkMode
then (SOME readVector, SOME readArray, NONE, NONE)
else (NONE, NONE, SOME(nonBlocking readVector),
SOME(nonBlocking readArray))
(* Don't allow random access on stdIn. The reason is that we
create stdIn when we compile TextIO yet this stream is persistent
(unlike every other stream). *)
val (getPos, setPos, endPos) =
if sys_get_iodesc fd <= 2 then (NONE, NONE, NONE)
else getRandAccessFns fd
(* Unlike the other functions "avail" is a function returning
an option, not an optional function. *)
fun avail () =
let
(* If we get an exception or a negative number return NONE. *)
val v = sys_avail fd handle OS.SysErr _ => ~1
in
if v >= 0 then SOME v else NONE
end
val textPrimRd =
TextPrimIO.RD {
name = name,
chunkSize = sys_get_buffsize fd,
readVec = readVec,
readArr = readArr,
readVecNB = readVecNB,
readArrNB = readArrNB,
block = SOME(fn () => sys_block_in fd),
canInput = SOME (fn () => sys_can_input fd > 0),
avail = avail,
getPos = getPos,
setPos = setPos,
endPos = endPos,
verifyPos = getPos,
close = fn () => sys_close fd,
ioDesc = (SOME fd) : OS.IO.iodesc option
}
in
TextPrimIO.augmentReader textPrimRd
end
fun wrapOutFileDescr {fd, name, appendMode, initBlkMode, chunkSize} =
let
fun writeArray (slice: CharArraySlice.slice): int =
let
val (buf, i, len) = CharArraySlice.base slice
val LibrarySupport.CharArray.Array(_, v) = buf
val iW = LibrarySupport.unsignedShortOrRaiseSubscript i
val lenW = LibrarySupport.unsignedShortOrRaiseSubscript len
in
sys_write_text(fd, (v, iW, lenW))
end
(* Write out a string using the underlying call. Note
that we have to add the size of a word to the offsets
to skip the length word. The underlying call deals
with the special case of a single character string
where the "string" is actually the character itself. *)
fun writeVector (slice: CharVectorSlice.slice): int =
let
val (buf, i, len) = CharVectorSlice.base slice
val v = LibrarySupport.stringAsAddress buf
val iW = LibrarySupport.unsignedShortOrRaiseSubscript i
val lenW = LibrarySupport.unsignedShortOrRaiseSubscript len
in
sys_write_text(fd, (v, iW+wordSize, lenW))
end
(* Set up the writers depending on whether the stream is
in non-blocking mode or not. *)
val (writeVec, writeArr, writeVecNB, writeArrNB) =
if initBlkMode
then (SOME writeVector, SOME writeArray, NONE, NONE)
else (NONE, NONE, SOME(nonBlocking writeVector),
SOME(nonBlocking writeArray))
(* Random access is provided if getPos works except that we
don't allow it for standard output and standard error at all. *)
val (getPos, setPos, endPos) =
if sys_get_iodesc fd <= 2 then (NONE, NONE, NONE)
else getRandAccessFns fd
(* If we have opened the stream for append we will always
write to the end of the stream so setPos won't work. *)
val setPos = if appendMode then NONE else setPos
val textPrimWr =
TextPrimIO.WR {
name = name,
chunkSize = chunkSize,
writeVec = writeVec,
writeArr = writeArr,
writeVecNB = writeVecNB,
writeArrNB = writeArrNB,
block = SOME(fn () => sys_block_out fd),
canOutput = SOME(fn () => sys_can_output fd > 0),
getPos = getPos,
setPos = setPos,
endPos = endPos,
verifyPos = getPos,
close = fn () => sys_close fd,
ioDesc = (SOME fd) : OS.IO.iodesc option
}
in
TextPrimIO.augmentWriter textPrimWr
end
fun wrapBinInFileDescr{fd, name, initBlkMode} =
let
fun readVector l = readBinVector(fd, l)
and readArray b = readBinArray(fd, b)
(* If we have opened the stream in non-blocking mode readVec
and readArray will raise an exception if they would block.
We have to handle that. The blocking functions can be
constructed using block_in but that should be done by
augmentReader. *)
val (readVec, readArr, readVecNB, readArrNB) =
if initBlkMode
then (SOME readVector, SOME readArray, NONE, NONE)
else (NONE, NONE, SOME(nonBlocking readVector),
SOME(nonBlocking readArray))
(* Random access is provided if getPos works. *)
val (getPos, setPos, endPos) = getRandAccessFns fd
(* Unlike the other functions "avail" is a function returning
an option, not an optional function. *)
fun avail () =
let
(* If we get an exception or a negative number return NONE. *)
val v = sys_avail fd handle OS.SysErr _ => ~1
in
if v >= 0 then SOME v else NONE
end
val binPrimRd =
BinPrimIO.RD {
name = name,
chunkSize = sys_get_buffsize fd,
readVec = readVec,
readArr = readArr,
readVecNB = readVecNB,
readArrNB = readArrNB,
block = SOME(fn () => sys_block_in fd),
canInput = SOME(fn() =>sys_can_input fd > 0),
avail = avail,
getPos = getPos,
setPos = setPos,
endPos = endPos,
verifyPos = getPos,
close = fn() => sys_close fd,
ioDesc = SOME fd
}
in
BinPrimIO.augmentReader binPrimRd
end
fun wrapBinOutFileDescr{fd, name, appendMode, initBlkMode, chunkSize} =
let
fun writeArray b = writeBinArray(fd, b)
and writeVector b = writeBinVec(fd, b)
(* Set up the writers depending on whether the stream is
in non-blocking mode or not. *)
val (writeVec, writeArr, writeVecNB, writeArrNB) =
if initBlkMode
then (SOME writeVector, SOME writeArray, NONE, NONE)
else (NONE, NONE, SOME(nonBlocking writeVector),
SOME(nonBlocking writeArray))
(* Random access is provided if getPos works. *)
val (getPos, setPos, endPos) = getRandAccessFns fd
(* If we have opened the stream for append we will always
write to the end of the stream so setPos won't work. *)
val setPos = if appendMode then NONE else setPos
val binPrimWr =
BinPrimIO.WR {
name = name,
chunkSize = chunkSize,
writeVec = writeVec,
writeArr = writeArr,
writeVecNB = writeVecNB,
writeArrNB = writeArrNB,
block = SOME(fn () => sys_block_out fd),
canOutput = SOME(fn () => sys_can_output fd > 0),
getPos = getPos,
setPos = setPos,
endPos = endPos,
verifyPos = getPos,
close = fn () => sys_close fd,
ioDesc = SOME fd
}
in
BinPrimIO.augmentWriter binPrimWr
end
(* Many of the IO functions need a mutex so we include this here.
This applies a function while a mutex is being held. *)
val protect = ThreadLib.protect
end;
|