-
Notifications
You must be signed in to change notification settings - Fork 11
/
bufferedfile.nim
131 lines (107 loc) · 3.56 KB
/
bufferedfile.nim
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
const
bufferSize* = 8192 ## size of a buffered file's buffer
type
BufferedFile* = object
file*: File
buffer*: array[bufferSize, char] # TODO: Make this noinit
curPos*: int ## current index in buffer
bufLen*: int ## current length of buffer
proc fread(buf: pointer, size, n: int, f: File): int {.
importc: "fread", header: "<stdio.h>".}
# TODO: What do we do with the C internal bufSize, always set to 0?
proc open*(bf: var BufferedFile; filename: string; mode: FileMode = fmRead;
bufSize: int = - 1): bool {.tags: [], gcsafe.} =
result = bf.file.open(filename, mode, bufSize)
bf.curPos = 0
bf.bufLen = 0
proc open*(bf: var BufferedFile; filehandle: FileHandle;
mode: FileMode = fmRead): bool {.tags: [], gcsafe.} =
result = bf.file.open(filehandle, mode)
bf.curPos = 0
bf.bufLen = 0
proc open*(filename: string, mode: FileMode = fmRead, bufSize: int = -1):
BufferedFile {.tags: [], gcsafe.} =
result.file = system.open(filename, mode, bufSize)
proc buffered*(file: File): BufferedFile {.tags: [], gcsafe.} =
result.file = file
proc clearBuffer(bf: var BufferedFile) =
bf.curPos = 0
bf.bufLen = 0
proc refillBuffer(bf: var BufferedFile) =
bf.curPos = 0
bf.bufLen = readChars(bf.file, bf.buffer, 0, bufferSize)
proc readChar*(bf: var BufferedFile): char =
if bf.curPos >= bf.bufLen:
bf.refillBuffer
result = bf.buffer[bf.curPos]
inc bf.curPos
proc raiseEIO(msg: string) {.noinline, noreturn.} =
raise newException(IOError, msg)
template addUntil(i): typed =
## Helper for readLine; Adds part of a char-array to a string efficiently
let nll = ll + i - bf.curPos
line.setLen(nll)
copyMem(addr line[ll], addr bf.buffer[bf.curPos], i - bf.curPos)
ll = nll
proc readLine*(bf: var BufferedFile, line: var string): bool
{.gcsafe.} =
var
i = bf.curPos
ll = 0
line.setLen(ll)
if bf.bufLen == 0:
bf.refillBuffer
if bf.bufLen == 0:
return false
while true:
for i in bf.curPos ..< bf.bufLen:
if bf.buffer[i] == '\l':
addUntil(i)
bf.curPos = i + 1
return true
if bf.buffer[i] == '\r':
addUntil(i)
if i+1 < bf.bufLen and bf.buffer[i+1] == '\l':
bf.curPos = i + 2
else:
bf.curPos = i + 1
return true
if bf.bufLen > 0:
addUntil(bf.bufLen)
bf.refillBuffer
if bf.bufLen == 0:
return ll > 0
proc readLine*(bf: var BufferedFile): string {.gcsafe.} =
result = newStringOfCap(80)
if not readLine(bf, result): raiseEIO("EOF reached")
proc readBuffer*(bf: var BufferedFile, buffer: pointer, len: int): int =
if bf.bufLen > 0:
result = bf.bufLen - bf.curPos
copyMem(buffer, addr bf.buffer[bf.curPos], min(len, result))
bf.clearBuffer
if len > result:
result += fread(buffer, 1, len - result, bf.file)
proc readBytes*(bf: var BufferedFile, a: var openArray[int8], start,
len: int): int =
result = readBuffer(bf, addr(a[start]), len)
proc readChars*(bf: var BufferedFile, a: var openArray[char], start,
len: int): int =
result = readBuffer(bf, addr(a[start]), len)
iterator lines*(bf: var BufferedFile): string =
var line = newStringOfCap(80)
while bf.readLine(line):
yield line
proc main() =
var count = 0
var sum = 0
var line = ""
var bstdin = stdin.buffered
#while stdin.readLine(line): # 3.8 s
#while bstdin.readLine(line): # 0.6 s
for line in bstdin.lines: # 0.6 s
count += 1
sum += line.len
echo "Average line length: ",
if count > 0: sum / count else: 0
when isMainModule:
main()