📄 text-io-fn.sml
字号:
val getPos = (case (getPos, setPos) of (SOME f, SOME _) => (fn () => SOME(f())) | _ => (fn () => NONE) (* end case *)) val more = SV.mVarInit NOMORE val closedFlg = ref false val tag = CleanIO.addCleaner dummyCleaner val info = INFO{ reader=reader, readVec=readVec, readVecEvt=readVecEvt, closed = closedFlg, getPos = getPos, tail = SV.mVarInit more, cleanTag = tag } val buf = (case optData of NONE => IBUF{ basePos = getPos(), data=empty, info=info, more=more }(** What should we do about the position in this case ?? **)(** Suggestion: When building a stream with supplied initial data, ** nothing can be said about the positions inside that initial ** data (who knows where that data even came from!). **) | (SOME v) => IBUF{ basePos = NONE, data=v, info=info, more=more} (* end case *)) val strm = ISTRM(buf, 0) in (tag, strm) end fun mkInstream arg = let val (tag, strm) = mkInstream' arg in CleanIO.rebindCleaner (tag, fn () => closeIn strm); strm end fun getReader (ISTRM(buf, pos)) = let val IBUF{data, info as INFO{reader, ...}, more, ...} = buf fun getData more = (case SV.mGet more of (MORE(IBUF{data, more=more', ...})) => data :: getData more' | _ => [] (* end case *)) in terminate info; if (pos < V.length data) then ( reader, V.concat(vecExtract(data, pos, NONE) :: getData more) ) else (reader, V.concat(getData more)) end (** Position operations on instreams **) datatype in_pos = INP of { base : pos, offset : int, info : info } fun getPosIn (ISTRM(buf, pos)) = (case buf of IBUF{basePos=NONE, info, ...} => inputExn (info, "getPosIn", IO.RandomAccessNotSupported) | IBUF{basePos=SOME p, info, ...} => INP{ base = p, offset = pos, info = info } (* end case *)) fun filePosIn (INP{base, offset, ...}) = Position.+(base, Position.fromInt offset) fun setPosIn (pos as INP{info as INFO{reader, ...}, ...}) = let val fpos = filePosIn pos val (PIO.RD rd) = reader in terminate info; valOf (#setPos rd) fpos; mkInstream (PIO.RD rd, NONE) end (** Text stream specific operations **) fun inputLine (ISTRM(buf as IBUF{data, ...}, pos)) = let fun join (item, (list, strm)) = (item::list, strm) fun nextBuf (isEmpty, buf as IBUF{more, data, ...}) = let fun last () = (if isEmpty then [] else ["\n"], ISTRM(buf, V.length data)) fun get (MORE buf) = scanData (buf, 0) | get NOMORE = (case (SV.mTake more) of NOMORE => ( case extendStream (readVec buf, "inputLine", buf) of EOF => last () | (DATA rest) => scanData (rest, 0) (* end case *)) | next => (SV.mPut(more, next); get next) (* end case *)) | get TERMINATED = last() in get (SV.mGet more) end and scanData (buf as IBUF{data, ...}, i) = let val len = V.length data fun scan j = if (j = len) then join(vecExtract(data, i, NONE), nextBuf(false, buf)) else if (vecSub(data, j) = #"\n") then ([vecExtract(data, i, SOME(j+1-i))], ISTRM(buf, j+1)) else scan (j+1) in scan i end val (data, strm) = if (V.length data = pos) then nextBuf (true, buf) else scanData (buf, pos) in (V.concat data, strm) end (*** Output streams ***) (* an output stream is implemented as a monitor using an mvar to * hold its data. *) datatype ostrm_info = OSTRM of { buf : A.array, pos : int ref, closed : bool ref, bufferMode : IO.buffer_mode ref, writer : writer, writeArr : {buf : A.array, i : int, sz : int option} -> unit, writeVec : {buf : V.vector, i : int, sz : int option} -> unit, cleanTag : CleanIO.tag } type outstream = ostrm_info SV.mvar fun isNL #"\n" = true | isNL _ = false fun isLineBreak (OSTRM{bufferMode, ...}) = if (!bufferMode = IO.LINE_BUF) then isNL else (fn _ => false) fun outputExn (OSTRM{writer=PIO.WR{name, ...}, ...}, mlOp, exn) = raise IO.Io{function=mlOp, name=name, cause=exn} (* lock access to the stream and make sure that it is not closed. *) fun lockAndChkClosedOut (strmMV, mlOp) = (case SV.mTake strmMV of (strm as OSTRM({closed=ref true, ...})) => ( SV.mPut (strmMV, strm); outputExn (strm, mlOp, IO.ClosedStream)) | strm => strm (* end case *)) fun flushBuffer (strmMV, strm as OSTRM{buf, pos, writeArr, ...}, mlOp) = ( case !pos of 0 => () | n => (( writeArr {buf=buf, i=0, sz=SOME n}; pos := 0) handle ex => ( SV.mPut(strmMV, strm); outputExn (strm, mlOp, ex))) (* end case *)) (* A version of copyVec that checks for newlines, while it is copying. * This is used for LINE_BUF output of strings and substrings. *) fun lineBufCopyVec (src, srcI, srcLen, dst, dstI) = let val stop = srcI+srcLen fun cpy (srcI, dstI, lb) = if (srcI < stop) then let val c = vecSub(src, srcI) in arrUpdate (dst, dstI, c); cpy (srcI+1, dstI+1, lb orelse isNL c) end else lb in cpy (srcI, dstI, false) end (* a version of copyVec for BLOCK_BUF output of strings and substrings. *) fun blockBufCopyVec (src, srcI, srcLen, dst, dstI) = ( A.copyVec { src = src, si = srcI, len = SOME srcLen, dst = dst, di = dstI }; false) fun output (strmMV, v) = let val (strm as OSTRM os) = lockAndChkClosedOut (strmMV, "output") fun release () = SV.mPut (strmMV, strm) val {buf, pos, bufferMode, ...} = os fun flush () = flushBuffer (strmMV, strm, "output") fun flushAll () = (#writeArr os {buf=buf, i=0, sz=NONE} handle ex => (release(); outputExn (strm, "output", ex))) fun writeDirect () = ( case !pos of 0 => () | n => (#writeArr os {buf=buf, i=0, sz=SOME n}; pos := 0) (* end case *); #writeVec os {buf=v, i=0, sz=NONE}) handle ex => (release(); outputExn (strm, "output", ex)) fun insert copyVec = let val bufLen = A.length buf val dataLen = V.length v in if (dataLen >= bufLen) then writeDirect() else let val i = !pos val avail = bufLen - i in if (avail < dataLen) then let val _ = A.copyVec{ src=v, si=0, len=SOME avail, dst=buf, di=i } val _ = flushAll() val needsFlush = copyVec(v, avail, dataLen-avail, buf, 0) in pos := dataLen-avail; if needsFlush then flush () else () end else let val needsFlush = copyVec(v, 0, dataLen, buf, i) in pos := i + dataLen; if (needsFlush orelse (avail = dataLen)) then flush() else () end end end in case !bufferMode of IO.NO_BUF => writeDirect () | IO.LINE_BUF => insert lineBufCopyVec | IO.BLOCK_BUF => insert blockBufCopyVec (* end case *); release() end fun output1 (strmMV, elem) = let val (strm as OSTRM{buf, pos, bufferMode, writeArr, ...}) = lockAndChkClosedOut (strmMV, "output1") fun release () = SV.mPut (strmMV, strm) in case !bufferMode of IO.NO_BUF => ( arrUpdate (buf, 0, elem); writeArr {buf=buf, i=0, sz=SOME 1} handle ex => (release(); outputExn (strm, "output1", ex))) | IO.LINE_BUF => let val i = !pos val i' = i+1 in arrUpdate (buf, i, elem); pos := i'; if ((i' = A.length buf) orelse (isNL elem)) then flushBuffer (strmMV, strm, "output1") else () end | IO.BLOCK_BUF => let val i = !pos val i' = i+1 in arrUpdate (buf, i, elem); pos := i'; if (i' = A.length buf) then flushBuffer (strmMV, strm, "output1") else () end (* end case *); release() end fun flushOut strmMV = let val strm = lockAndChkClosedOut (strmMV, "flushOut") in flushBuffer (strmMV, strm, "flushOut"); SV.mPut (strmMV, strm) end fun closeOut strmMV = let val (strm as OSTRM{writer=PIO.WR{close, ...}, closed, cleanTag, ...}) = SV.mTake strmMV in if !closed then () else ( flushBuffer (strmMV, strm, "closeOut"); closed := true; CleanIO.removeCleaner cleanTag; close()); SV.mPut (strmMV, strm) end fun mkOutstream' (wr as PIO.WR{chunkSize, writeArr, writeVec, ...}, mode) = let fun iterate f (buf, i, sz) = let fun lp (_, 0) = () | lp (i, n) = let val n' = f{buf=buf, i=i, sz=SOME n} in lp (i+n', n-n') end in lp (i, sz) end fun writeArr' {buf, i, sz} = let val len = (case sz of NONE => A.length buf - i | (SOME n) => n (* end case *)) in iterate writeArr (buf, i, len) end fun writeVec' {buf, i, sz} = let val len = (case sz of NONE => V.length buf - i | (SOME n) => n (* end case *)) in iterate writeVec (buf, i, len) end (* install a dummy cleaner *) val tag = CleanIO.addCleaner dummyCleaner val strm = SV.mVarInit (OSTRM{ buf = A.array(chunkSize, someElem), pos = ref 0, closed = ref false, bufferMode = ref mode, writer = wr, writeArr = writeArr', writeVec = writeVec', cleanTag = tag }) in (tag, strm) end fun mkOutstream arg = let val (tag, strm) = mkOutstream' arg in CleanIO.rebindCleaner (tag, fn () => closeOut strm); strm end fun getWriter strmMV = let val (strm as OSTRM{writer, bufferMode, ...}) = lockAndChkClosedOut (strmMV, "getWriter") in
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -