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
|
#
#
# The Nim Compiler
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# Ropes for the C code generator. Ropes are mapped to `string` directly nowadays.
from pathutils import AbsoluteFile
when defined(nimPreviewSlimSystem):
import std/[assertions, syncio, formatfloat]
type
FormatStr* = string # later we may change it to CString for better
# performance of the code generator (assignments
# copy the format strings
# though it is not necessary)
Rope* = string
proc newRopeAppender*(): string {.inline.} =
result = newString(0)
proc freeze*(r: Rope) {.inline.} = discard
proc resetRopeCache* = discard
template rope*(s: string): string = s
proc rope*(i: BiggestInt): Rope =
## Converts an int to a rope.
result = rope($i)
proc rope*(f: BiggestFloat): Rope =
## Converts a float to a rope.
result = rope($f)
proc writeRope*(f: File, r: Rope) =
## writes a rope to a file.
write(f, r)
proc writeRope*(head: Rope, filename: AbsoluteFile): bool =
var f: File
if open(f, filename.string, fmWrite):
writeRope(f, head)
close(f)
result = true
else:
result = false
proc prepend*(a: var Rope, b: string) = a = b & a
proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope =
var i = 0
result = newRopeAppender()
var num = 0
while i < frmt.len:
if frmt[i] == '$':
inc(i) # skip '$'
case frmt[i]
of '$':
result.add("$")
inc(i)
of '#':
inc(i)
result.add(args[num])
inc(num)
of '0'..'9':
var j = 0
while true:
j = j * 10 + ord(frmt[i]) - ord('0')
inc(i)
if i >= frmt.len or frmt[i] notin {'0'..'9'}: break
num = j
if j > high(args) + 1:
doAssert false, "invalid format string: " & frmt
else:
result.add(args[j-1])
of '{':
inc(i)
var j = 0
while frmt[i] in {'0'..'9'}:
j = j * 10 + ord(frmt[i]) - ord('0')
inc(i)
num = j
if frmt[i] == '}': inc(i)
else:
doAssert false, "invalid format string: " & frmt
if j > high(args) + 1:
doAssert false, "invalid format string: " & frmt
else:
result.add(args[j-1])
of 'n':
result.add("\n")
inc(i)
of 'N':
result.add("\n")
inc(i)
else:
doAssert false, "invalid format string: " & frmt
var start = i
while i < frmt.len:
if frmt[i] != '$': inc(i)
else: break
if i - 1 >= start:
result.add(substr(frmt, start, i - 1))
proc `%`*(frmt: static[FormatStr], args: openArray[Rope]): Rope =
runtimeFormat(frmt, args)
template addf*(c: var Rope, frmt: FormatStr, args: openArray[Rope]) =
## shortcut for ``add(c, frmt % args)``.
c.add(frmt % args)
const
bufSize = 1024 # 1 KB is reasonable
proc equalsFile*(s: Rope, f: File): bool =
## returns true if the contents of the file `f` equal `r`.
var
buf: array[bufSize, char]
bpos = buf.len
blen = buf.len
btotal = 0
rtotal = 0
when true:
var spos = 0
rtotal += s.len
while spos < s.len:
if bpos == blen:
# Read more data
bpos = 0
blen = readBuffer(f, addr(buf[0]), buf.len)
btotal += blen
if blen == 0: # no more data in file
result = false
return
let n = min(blen - bpos, s.len - spos)
# TODO There's gotta be a better way of comparing here...
if not equalMem(addr(buf[bpos]), cast[pointer](cast[int](cstring(s))+spos), n):
result = false
return
spos += n
bpos += n
result = readBuffer(f, addr(buf[0]), 1) == 0 and
btotal == rtotal # check that we've read all
proc equalsFile*(r: Rope, filename: AbsoluteFile): bool =
## returns true if the contents of the file `f` equal `r`. If `f` does not
## exist, false is returned.
var f: File
result = open(f, filename.string)
if result:
result = equalsFile(r, f)
close(f)
|