The PrimIO 시그니처

The PrimIO 시그니처

이 시그니처는 SML I/O 시스템의 밑에 깔린 프리미티브 I/O 연산을 정의해요.

출처: 문서

본문

시놉시스 (Synopsis)

signature PRIM_IO
structure BinPrimIO :> PRIM_IO
where type array = Word8Array.array
where type vector = Word8Vector.vector
where type elem = Word8.word
where type pos = Position.int
structure TextPrimIO :> PRIM_IO
where type array = CharArray.array
where type vector = CharVector.vector
where type elem = Char.char
structure WideTextPrimIO :> PRIM_IO (* OPTIONAL *)
where type array = WideCharArray.array
where type vector = WideCharVector.vector
where type elem = WideChar.char

인터페이스 (Interface)

type elem
type vector
type vector_slice
type array
type array_slice
eqtype pos
val compare : pos * pos -> order
datatype reader
= RD of {
name : string,
chunkSize : int,
readVec : (int -> vector) option,
readArr : (array_slice -> int) option,
readVecNB : (int -> vector option) option,
readArrNB : (array_slice -> int option) option,
block : (unit -> unit) option,
canInput : (unit -> bool) option,
avail : unit -> int option,
getPos : (unit -> pos) option,
setPos : (pos -> unit) option,
endPos : (unit -> pos) option,
verifyPos : (unit -> pos) option,
close : unit -> unit,
ioDesc : OS.IO.iodesc option
}
datatype writer
= WR of {
name : string,
chunkSize : int,
writeVec : (vector_slice -> int) option,
writeArr : (array_slice -> int) option,
writeVecNB : (vector_slice -> int option) option,
writeArrNB : (array_slice -> int option) option,
block : (unit -> unit) option,
canOutput : (unit -> bool) option,
getPos : (unit -> pos) option,
setPos : (pos -> unit) option,
endPos : (unit -> pos) option,
verifyPos : (unit -> pos) option,
close : unit -> unit,
ioDesc : OS.IO.iodesc option
}
val openVector : vector -> reader
val nullRd : unit -> reader
val nullWr : unit -> writer
val augmentReader : reader -> reader
val augmentWriter : writer -> writer

설명 (Description)

type elem

읽기·쓰기 연산의 요소 타입이에요.

type vector
type vector_slice
type array
type array_slice

데이터를 담는 버퍼를 나타내는 타입이에요.

eqtype pos

readVec은 벡터를 한 번에 읽고, readArr는 배열에 읽어 넣어요.

compare (pos, pos')

writeVec·writeArr는 벡터·배열을 한 번에 써요.

datatype reader
= RD of {
name : string,
chunkSize : int,
readVec : (int -> vector) option,
readArr : (array_slice -> int) option,
readVecNB : (int -> vector option) option,
readArrNB : (array_slice -> int option) option,
block : (unit -> unit) option,
canInput : (unit -> bool) option,
avail : unit -> int option,
getPos : (unit -> pos) option,
setPos : (pos -> unit) option,
endPos : (unit -> pos) option,
verifyPos : (unit -> pos) option,
close : unit -> unit,
ioDesc : OS.IO.iodesc option
}

getPos·setPos는 스트림의 현재 위치를 조회·설정해요.

name

endOfStream은 스트림 끝 도달 여부를 알려줘요.

chunkSize

버퍼링과 닫기, 플러시 연산을 담아요.

readVec(n)

존재하면 n개까지 요소를 읽어 읽은 요소의 벡터를 돌려줘요. 이 함수는 스트림 끝이 감지되면(또는 n이 0이면) 빈 벡터를 돌려줘요. 필요하면 스트림 끝이 감지되거나 최소한 하나의 요소가 이용 가능해질 때까지 차단해요. 이 함수의 구현은 n < 0이면 Size 예외를 발생시키는 것이 권장돼요.

readArr(slice)

존재하면 배열 슬라이스 slice에 k개까지 요소를 읽는데, 여기서 k는 슬라이스의 크기예요. 이 함수는 실제로 읽은 요소 수를 돌려주며, 이는 k보다 작거나 같아요. 스트림 끝 이전에 남은 요소가 없으면 0을 돌려줘요(이 함수는 slice가 비어 있을 때도 0을 돌려줘요). 필요하면 최소한 하나의 요소가 이용 가능해질 때까지 차단해요.

readVecNB(n)

존재하면 1 <= i <= n인 i개 요소를 차단하지 않고 읽어 벡터 v를 만들고 SOME(v)를 돌려줘요. 또는 차단하지 않고 스트림 끝이 감지되면 SOME(fromList[])를 돌려줘요. 또는 읽기가 차단되면 NONE을 돌려줘요.

readArrNB(slice)

존재하면 배열 슬라이스 slice에 k개까지 요소를 차단하지 않고 읽는데, 여기서 k는 슬라이스의 크기예요. 이 함수가 차단할 경우(즉 이용 가능한 요소가 없고 스트림 끝이 감지되지 않은 경우) NONE을 돌려주고, 그렇지 않으면 실제로 읽은 요소 수인 SOME(n)을 돌려줘요(스트림 끝에서는 0).

block()

존재하면 차단하지 않고 읽기에 최소한 하나의 요소가 이용 가능해지거나 스트림 끝 조건이 감지될 때까지 차단해요.

canInput()

존재하면 다음 읽기가 차단 없이 진행될 수 있을 때에만 true를 돌려줘요.

avail()

``장치''에서 이용 가능한 바이트 수를 돌려주고, 결정할 수 없으면 NONE을 돌려줘요. 파일이나 문자열의 경우 이는 파일 또는 문자열 크기에서 현재 위치를 뺀 값이고, 대부분의 다른 입력 소스에서는 아마 NONE일 거예요. 이는 inputAll이 힌트로 사용할 수 있어요. 이는 요소 수가 아니라 바이트 수라는 점에 유의하세요.

getPos()

존재하면 파일의 현재 위치를 돌려줘요. getPos 함수는 (기본 객체에 setPos 연산이나 다른 간섭이 없으면) 비감소여야 해요.

setPos(i)

존재하면 파일의 위치 i로 이동해요.

endPos()

존재하면 현재 위치를 실제로 바꾸지 않고 파일 끝에 해당하는 위치를 돌려줘요.

verifyPos()

존재하면 파일의 실제 현재 위치를 돌려줘요. getPos와 비슷하지만, 후자는 효율성을 위해 자체적인 파일 위치 개념을 유지할 수 있는 반면 verifyPos는 보통 시스템 호출을 수행해 기본 운영체제의 파일 위치 값을 얻어요.

close

판독기를 닫힘으로 표시하고, 필요하면 정리를 수행하고 운영체제 자원을 해제해요. (close와 getPos를 제외한) 판독기에 대한 추가 연산은 IO.ClosedStream을 발생시켜요.

ioDesc

존재하면 이 스트림과 연관된 추상 운영체제 디스크립터예요. readVec, readVecNB, readArr, readArrNB 중 하나는 제공해야 해요. 선택 함수를 더 많이 제공하면 클라이언트의 기능과/또는 효율성이 높아져요. readVec, readArr, block이 모두 없으면 차단 입력이 불가능해요. readVecNB, readArrNB, canInput이 모두 없으면 비차단 입력이 불가능해요. readVecNB가 없으면 비차단 입력에 두 번의 시스템 호출(canInput과 readVec 사용)이 필요해요. readArr나 readArrNB가 없으면 배열로의 입력에 추가 복사가 필요해요. 지연 함수형 스트림'' 모델은 배열을 전혀 사용하지 않는다는 점에 유의하세요. setPos가 없으면 임의 접근이 불가능해요. avail이 값을 돌려주면 클라이언트가 한 번의 시스템 호출과 복사 없이 매우 큰 입력을 더 효율적으로 수행하는 데 도움이 돼요. 판독기가 명백한 합성(augmentReader 참조)보다 더 효율적인 방식으로 최소 연산 집합보다 더 많이 제공할 수 있다면 반드시 그래야 해요. 기본 I/O 계층 안에서 명백한 합성만으로 최소보다 더 많이 제공하는 것은 권장되지 않아요. 클라이언트가 어떤 연산이 효율적인(권장되는'') 연산인지에 대한 ``힌트''를 얻지 못하기 때문이에요. 효율성에 관심이 있는 클라이언트는 네이티브로 제공되는 연산을 사용하며 제공되는 연산에 따라 알고리즘을 선택해야 할 수 있고, 효율성이나 특정 연산에 관심이 없는 클라이언트는 augmentReader가 만든 판독기를 사용할 수 있어요.

datatype writer
= WR of {
name : string,
chunkSize : int,
writeVec : (vector_slice -> int) option,
writeArr : (array_slice -> int) option,
writeVecNB : (vector_slice -> int option) option,
writeArrNB : (array_slice -> int option) option,
block : (unit -> unit) option,
canOutput : (unit -> bool) option,
getPos : (unit -> pos) option,
setPos : (pos -> unit) option,
endPos : (unit -> pos) option,
verifyPos : (unit -> pos) option,
close : unit -> unit,
ioDesc : OS.IO.iodesc option
}

기록기는 쓰기 위해 열린 파일(장치 등)이에요. 기록기는 elem 타입 항목 저장소의 추상화예요. 보통 쓰기 위해 열린 파일이나 장치에 해당해요. 외부 세계와 반드시 연결되지 않아도 되며, 계산을 안내하기 위해 출력을 소비하는 어떤 알고리즘 또는 함수에 대한 입력을 나타낼 수도 있어요. 결과 요소 저장소는 잠재적으로 무한할 수 있어요. 아래 설명에서 저장소를 보통 ``파일''이라고 부를 텐데, 이는 가장 흔한 경우이기 때문이에요.

name

사용자에게 보여주는 오류 메시지에 쓰이는, 이 파일 또는 장치와 연관된 이름이에요.

chunkSize

이 기록기에 대한 쓰기 연산의 권장(효율적인) 크기예요. 보통 운영체제 버퍼의 블록 크기로 설정돼요. chunkSize = 1은 버퍼링 없는 쓰기를 강력히 권장하지만, 버퍼링은 더 높은 수준에서 처리되므로 보장할 수는 없어요. chunkSize <= 0은 불법이에요.

writeVec(slice)

존재하면 벡터 슬라이스 slice의 요소를 출력 장치에 쓰고 실제로 쓰인 요소 수를 돌려줘요. 필요하면 출력 장치가 최소한 하나의 요소를 받아들일 수 있을 때까지 차단해요.

writeArr(slice)

존재하면 배열 슬라이스 slice의 요소를 쓰고 실제로 쓰인 요소 수를 돌려줘요. 필요하면 기본 장치가 최소한 하나의 요소를 받아들일 수 있을 때까지 차단해요.

writeVecNB(slice)

존재하면 벡터 슬라이스 slice의 요소를 차단하지 않고 출력 장치에 쓰려고 시도해요. 성공하면 실제로 쓰인 요소 수인 SOME(n)을 돌려줘요. 그렇지 않고 차단될 경우 차단하지 않고 NONE을 돌려줘요.

writeVecNB(slice)

존재하면 배열 슬라이스 slice의 요소를 차단하지 않고 출력 장치에 쓰려고 시도해요. 성공하면 실제로 쓰인 요소 수인 SOME(n)을 돌려줘요. 그렇지 않고 차단될 경우 차단하지 않고 NONE을 돌려줘요.

block()

존재하면 기록기가 차단 없이 쓸 수 있음이 보장될 때까지 차단해요.

canOutput()

존재하면 다음 쓰기가 차단 없이 진행될 수 있을 때에만 true를 돌려줘요.

getPos()

존재하면 파일 내의 현재 위치를 돌려줘요.

endPos()

존재하면 현재 위치를 실제로 바꾸지 않고 파일 끝에 해당하는 위치를 돌려줘요.

setPos(i)

존재하면 파일의 위치 i로 이동해서, 이후 쓰기가 이 위치에서 발생하게 해요.

verifyPos()

존재하면 파일의 실제 현재 위치를 돌려줘요. getPos와 비슷하지만, 후자는 효율성을 위해 자체적인 파일 위치 개념을 유지할 수 있는 반면 verifyPos는 보통 시스템 호출을 수행해 기본 운영체제의 파일 위치 값을 얻어요.

close()

기록기를 닫힘으로 표시하고, 필요하면 정리를 수행하고 운영체제 자원을 해제해요. (close를 제외한) 추가 연산은 IO.ClosedStream을 발생시켜요.

ioDesc

존재하면 이 스트림과 연관된 추상 운영체제 디스크립터예요. 쓰기 연산은 쓰인 전체 요소 수를 돌려줘요. 요소 크기가 1바이트보다 크면 요소의 추가 부분이 쓰일 수 있어요. 예를 들어 각각 3바이트 크기의 요소 2개를 쓰려고 하면 기본 시스템 쓰기 연산이 6바이트 중 4바이트만 쓰였다고 보고할 수 있어요. 따라서 전체 요소 하나와 두 번째 요소의 일부가 쓰였으므로 쓰기 연산은 1을 돌려줘요. writeVec, writeVecNB, writeArr, writeArrNB 중 하나는 제공해야 해요. 선택 함수를 더 많이 제공하면 클라이언트의 기능과/또는 효율성이 높아져요. writeVec, writeArr, block이 모두 없으면 차단 출력이 불가능해요. writeVecNB, writeArrNB, canOutput이 모두 없으면 비차단 출력이 불가능해요. writeArr나 writeArrNB가 없으면 배열에서 쓰려면 추가 복사가 필요해요. setPos가 없으면 임의 접근이 불가능해요.

openVector v

내용이 v인 판독기를 만들어요.

val nullRd : unit -> reader
val nullWr : unit -> writer

이 함수들은 null 장치 추상화를 위한 판독기와 기록기를 만들어요. 판독기 nullRd는 항상 스트림 끝에 있는 판독기처럼 작동해요. 기록기 nullWr은 싱크 역할을 해서, 이를 사용해 쓴 데이터는 버려져요. null 판독기와 기록기는 닫을 수 있고, 닫히면 다른 닫힌 판독기나 기록기와 똑같이 작동할 것으로 기대돼요.

augmentReader rd

rd의 연산들에서 readVec, readArr, readVecNB, readArrNB를 가능한 한 많이 합성해 제공하는 판독기를 만들어요. 예를 들어 augmentReader는 readVecNB와 block에서 readVec을 합성하고, 배열 읽기에서 벡터 읽기를 합성하고, 벡터 읽기에서 배열 읽기를 필요에 따라 합성할 수 있어요. 다음 표는 각 합성을 어떻게 수행할 수 있는지 나타내요. 합성: 출처: readVec readVec 또는 readArr 또는 (block과 (readVecNB 또는 readArrNB)), readArr readArr 또는 readVec 또는 (block과 (readArrNB 또는 readVecNB)), readVecNB readVecNB 또는 readArrNB 또는 (canInput과 (readVec 또는 readArr)), readArrNB readArrNB 또는 readVecNB 또는 (canInput과 (readArr 또는 readVec)). 각 경우 합성된 연산은 더 직접적인 구현만큼 효율적이지 않을 수 있어요. 예를 들어 데이터를 배열로 직접 읽는 것이 벡터로 읽은 다음 배열로 복사하는 것보다 빠르기 때문이에요. 하지만 augmentReader는 해를 끼치지 않아야 해요: 판독기 rd가 어떤 연산(예: readArr)을 제공하면 augmentReader(rd)는 합성된 것이 아니라 그 연산의 같은 구현을 제공해요.

augmentWriter wr

wr의 연산들에서 writeVec, writeArr, writeVecNB, writeArrNB를 가능한 한 많이 합성해 제공하는 기록기를 만들어요. 다음 표는 각 합성을 어떻게 수행할 수 있는지 나타내요. 합성: 출처: writeVec writeVec 또는 writeArr 또는 (block과 (writeVecNB 또는 writeArrNB)), writeArr writeArr 또는 writeVec 또는 (block과 (writeArrNB 또는 writeVecNB)), writeVecNB writeVecNB 또는 writeArrNB 또는 (canOutput과 (writeVec 또는 writeArr)), writeArrNB writeArrNB 또는 writeVecNB 또는 (canOutput과 (writeArr 또는 writeVec)). 합성된 연산은 더 직접적인 구현만큼 효율적이지 않을 수 있지만, 기록기가 어떤 연산을 제공하면 확장된 기록기는 그 연산의 같은 구현을 제공해요.## 더 알아보기 (Learn more)