added missing files;change config for bug #374441
This commit is contained in:
parent
d54b333e7e
commit
c7e144f978
11 changed files with 324 additions and 15 deletions
|
|
@ -522,7 +522,7 @@ Result variable
|
||||||
---------------
|
---------------
|
||||||
A procedure that returns a value has an implicit ``result`` variable that
|
A procedure that returns a value has an implicit ``result`` variable that
|
||||||
represents the return value. A ``return`` statement with no expression is a
|
represents the return value. A ``return`` statement with no expression is a
|
||||||
shorthand for ``return result``. So all tree code snippets are equivalent:
|
shorthand for ``return result``. So all three code snippets are equivalent:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nimrod
|
||||||
return 42
|
return 42
|
||||||
|
|
@ -952,8 +952,8 @@ Operation Comment
|
||||||
``dec(x, n)`` decrements `x` by `n`; `n` is an integer
|
``dec(x, n)`` decrements `x` by `n`; `n` is an integer
|
||||||
``succ(x)`` returns the successor of `x`
|
``succ(x)`` returns the successor of `x`
|
||||||
``succ(x, n)`` returns the `n`'th successor of `x`
|
``succ(x, n)`` returns the `n`'th successor of `x`
|
||||||
``succ(x)`` returns the predecessor of `x`
|
``prec(x)`` returns the predecessor of `x`
|
||||||
``succ(x, n)`` returns the `n`'th predecessor of `x`
|
``pred(x, n)`` returns the `n`'th predecessor of `x`
|
||||||
----------------- --------------------------------------------------------
|
----------------- --------------------------------------------------------
|
||||||
|
|
||||||
The ``inc dec succ pred`` operations can fail by raising an `EOutOfRange` or
|
The ``inc dec succ pred`` operations can fail by raising an `EOutOfRange` or
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ While Nimrod's support for object oriented programming (OOP) is minimalistic,
|
||||||
powerful OOP technics can be used. OOP is seen as *one* way to design a
|
powerful OOP technics can be used. OOP is seen as *one* way to design a
|
||||||
program, not *the only* way. Often a procedural approach leads to simpler
|
program, not *the only* way. Often a procedural approach leads to simpler
|
||||||
and more efficient code. In particular, prefering aggregation over inheritance
|
and more efficient code. In particular, prefering aggregation over inheritance
|
||||||
often yields to a better design.
|
often results in a better design.
|
||||||
|
|
||||||
|
|
||||||
Objects
|
Objects
|
||||||
|
|
@ -422,7 +422,7 @@ containers:
|
||||||
else:
|
else:
|
||||||
var it = root
|
var it = root
|
||||||
while it != nil:
|
while it != nil:
|
||||||
# compare the data items; uses the generic ``cmd`` proc
|
# compare the data items; uses the generic ``cmp`` proc
|
||||||
# that works for any type that has a ``==`` and ``<`` operator
|
# that works for any type that has a ``==`` and ``<`` operator
|
||||||
var c = cmp(it.data, n.data)
|
var c = cmp(it.data, n.data)
|
||||||
if c < 0:
|
if c < 0:
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
import os, streams, parsecsv, strutils, math
|
import os, streams, parsecsv, strutils, math
|
||||||
|
|
||||||
if paramCount() < 1:
|
if paramCount() < 1:
|
||||||
quit("Usage: sumcsv filename[.csv]")
|
quit("Usage: statcsv filename[.csv]")
|
||||||
|
|
||||||
var filename = appendFileExt(ParamStr(1), "csv")
|
var filename = appendFileExt(ParamStr(1), "csv")
|
||||||
var s = newFileStream(filename, fmRead)
|
var s = newFileStream(filename, fmRead)
|
||||||
|
|
|
||||||
|
|
@ -493,6 +493,10 @@ proc rawDealloc(a: var TAllocator, p: pointer) =
|
||||||
f.zeroField = 0
|
f.zeroField = 0
|
||||||
f.next = c.freeList
|
f.next = c.freeList
|
||||||
c.freeList = f
|
c.freeList = f
|
||||||
|
when overwriteFree:
|
||||||
|
# set to 0xff to check for usage after free bugs:
|
||||||
|
c_memset(cast[pointer](cast[int](p) +% sizeof(TFreeCell)), -1'i32,
|
||||||
|
s -% sizeof(TFreeCell))
|
||||||
# check if it is not in the freeSmallChunks[s] list:
|
# check if it is not in the freeSmallChunks[s] list:
|
||||||
if c.free < s:
|
if c.free < s:
|
||||||
assert c notin a.freeSmallChunks[s div memAlign]
|
assert c notin a.freeSmallChunks[s div memAlign]
|
||||||
|
|
@ -506,6 +510,8 @@ proc rawDealloc(a: var TAllocator, p: pointer) =
|
||||||
c.size = SmallChunkSize
|
c.size = SmallChunkSize
|
||||||
freeBigChunk(a, cast[PBigChunk](c))
|
freeBigChunk(a, cast[PBigChunk](c))
|
||||||
else:
|
else:
|
||||||
|
# set to 0xff to check for usage after free bugs:
|
||||||
|
when overwriteFree: c_memset(p, -1'i32, c.size -% bigChunkOverhead())
|
||||||
# free big chunk
|
# free big chunk
|
||||||
freeBigChunk(a, cast[PBigChunk](c))
|
freeBigChunk(a, cast[PBigChunk](c))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ proc c_strcmp(a, b: CString): cint {.nodecl, importc: "strcmp".}
|
||||||
proc c_memcmp(a, b: CString, size: cint): cint {.nodecl, importc: "memcmp".}
|
proc c_memcmp(a, b: CString, size: cint): cint {.nodecl, importc: "memcmp".}
|
||||||
proc c_memcpy(a, b: CString, size: cint) {.nodecl, importc: "memcpy".}
|
proc c_memcpy(a, b: CString, size: cint) {.nodecl, importc: "memcpy".}
|
||||||
proc c_strlen(a: CString): cint {.nodecl, importc: "strlen".}
|
proc c_strlen(a: CString): cint {.nodecl, importc: "strlen".}
|
||||||
|
proc c_memset(p: pointer, value: cint, size: int) {.nodecl, importc: "memset".}
|
||||||
|
|
||||||
type
|
type
|
||||||
C_TextFile {.importc: "FILE", nodecl, final.} = object # empty record for
|
C_TextFile {.importc: "FILE", nodecl, final.} = object # empty record for
|
||||||
|
|
|
||||||
196
lib/cellsets.nim
Normal file
196
lib/cellsets.nim
Normal file
|
|
@ -0,0 +1,196 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nimrod's Runtime Library
|
||||||
|
# (c) Copyright 2009 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Efficient set of pointers for the GC (and repr)
|
||||||
|
|
||||||
|
type
|
||||||
|
TCell {.pure.} = object
|
||||||
|
refcount: int # the refcount and some flags
|
||||||
|
typ: PNimType
|
||||||
|
when debugGC:
|
||||||
|
filename: cstring
|
||||||
|
line: int
|
||||||
|
|
||||||
|
PCell = ptr TCell
|
||||||
|
|
||||||
|
PPageDesc = ptr TPageDesc
|
||||||
|
TBitIndex = range[0..UnitsPerPage-1]
|
||||||
|
TPageDesc {.final, pure.} = object
|
||||||
|
next: PPageDesc # all nodes are connected with this pointer
|
||||||
|
key: TAddress # start address at bit 0
|
||||||
|
bits: array[TBitIndex, int] # a bit vector
|
||||||
|
|
||||||
|
PPageDescArray = ptr array[0..1000_000, PPageDesc]
|
||||||
|
TCellSet {.final, pure.} = object
|
||||||
|
counter, max: int
|
||||||
|
head: PPageDesc
|
||||||
|
data: PPageDescArray
|
||||||
|
|
||||||
|
PCellArray = ptr array[0..100_000_000, PCell]
|
||||||
|
TCellSeq {.final, pure.} = object
|
||||||
|
len, cap: int
|
||||||
|
d: PCellArray
|
||||||
|
|
||||||
|
# ------------------- cell set handling ---------------------------------------
|
||||||
|
|
||||||
|
proc contains(s: TCellSeq, c: PCell): bool {.inline.} =
|
||||||
|
for i in 0 .. s.len-1:
|
||||||
|
if s.d[i] == c: return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
proc add(s: var TCellSeq, c: PCell) {.inline.} =
|
||||||
|
if s.len >= s.cap:
|
||||||
|
s.cap = s.cap * 3 div 2
|
||||||
|
var d = cast[PCellArray](alloc(s.cap * sizeof(PCell)))
|
||||||
|
copyMem(d, s.d, s.len * sizeof(PCell))
|
||||||
|
dealloc(s.d)
|
||||||
|
s.d = d
|
||||||
|
# XXX: realloc?
|
||||||
|
s.d[s.len] = c
|
||||||
|
inc(s.len)
|
||||||
|
|
||||||
|
proc init(s: var TCellSeq, cap: int = 1024) =
|
||||||
|
s.len = 0
|
||||||
|
s.cap = cap
|
||||||
|
s.d = cast[PCellArray](alloc0(cap * sizeof(PCell)))
|
||||||
|
|
||||||
|
proc deinit(s: var TCellSeq) =
|
||||||
|
dealloc(s.d)
|
||||||
|
s.d = nil
|
||||||
|
s.len = 0
|
||||||
|
s.cap = 0
|
||||||
|
|
||||||
|
const
|
||||||
|
InitCellSetSize = 1024 # must be a power of two!
|
||||||
|
|
||||||
|
proc Init(s: var TCellSet) =
|
||||||
|
s.data = cast[PPageDescArray](alloc0(InitCellSetSize * sizeof(PPageDesc)))
|
||||||
|
s.max = InitCellSetSize-1
|
||||||
|
s.counter = 0
|
||||||
|
s.head = nil
|
||||||
|
|
||||||
|
proc Deinit(s: var TCellSet) =
|
||||||
|
var it = s.head
|
||||||
|
while it != nil:
|
||||||
|
var n = it.next
|
||||||
|
dealloc(it)
|
||||||
|
it = n
|
||||||
|
s.head = nil # play it safe here
|
||||||
|
dealloc(s.data)
|
||||||
|
s.data = nil
|
||||||
|
s.counter = 0
|
||||||
|
|
||||||
|
proc nextTry(h, maxHash: int): int {.inline.} =
|
||||||
|
result = ((5*h) + 1) and maxHash
|
||||||
|
# For any initial h in range(maxHash), repeating that maxHash times
|
||||||
|
# generates each int in range(maxHash) exactly once (see any text on
|
||||||
|
# random-number generation for proof).
|
||||||
|
|
||||||
|
proc CellSetGet(t: TCellSet, key: TAddress): PPageDesc =
|
||||||
|
var h = cast[int](key) and t.max
|
||||||
|
while t.data[h] != nil:
|
||||||
|
if t.data[h].key == key: return t.data[h]
|
||||||
|
h = nextTry(h, t.max)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
proc CellSetRawInsert(t: TCellSet, data: PPageDescArray, desc: PPageDesc) =
|
||||||
|
var h = cast[int](desc.key) and t.max
|
||||||
|
while data[h] != nil:
|
||||||
|
assert(data[h] != desc)
|
||||||
|
h = nextTry(h, t.max)
|
||||||
|
assert(data[h] == nil)
|
||||||
|
data[h] = desc
|
||||||
|
|
||||||
|
proc CellSetEnlarge(t: var TCellSet) =
|
||||||
|
var oldMax = t.max
|
||||||
|
t.max = ((t.max+1)*2)-1
|
||||||
|
var n = cast[PPageDescArray](alloc0((t.max + 1) * sizeof(PPageDesc)))
|
||||||
|
for i in 0 .. oldmax:
|
||||||
|
if t.data[i] != nil:
|
||||||
|
CellSetRawInsert(t, n, t.data[i])
|
||||||
|
dealloc(t.data)
|
||||||
|
t.data = n
|
||||||
|
|
||||||
|
proc CellSetPut(t: var TCellSet, key: TAddress): PPageDesc =
|
||||||
|
var h = cast[int](key) and t.max
|
||||||
|
while true:
|
||||||
|
var x = t.data[h]
|
||||||
|
if x == nil: break
|
||||||
|
if x.key == key: return x
|
||||||
|
h = nextTry(h, t.max)
|
||||||
|
|
||||||
|
if ((t.max+1)*2 < t.counter*3) or ((t.max+1)-t.counter < 4):
|
||||||
|
CellSetEnlarge(t)
|
||||||
|
inc(t.counter)
|
||||||
|
h = cast[int](key) and t.max
|
||||||
|
while t.data[h] != nil: h = nextTry(h, t.max)
|
||||||
|
assert(t.data[h] == nil)
|
||||||
|
# the new page descriptor goes into result
|
||||||
|
result = cast[PPageDesc](alloc0(sizeof(TPageDesc)))
|
||||||
|
result.next = t.head
|
||||||
|
result.key = key
|
||||||
|
t.head = result
|
||||||
|
t.data[h] = result
|
||||||
|
|
||||||
|
# ---------- slightly higher level procs --------------------------------------
|
||||||
|
|
||||||
|
proc contains(s: TCellSet, cell: PCell): bool =
|
||||||
|
var u = cast[TAddress](cell)
|
||||||
|
var t = CellSetGet(s, u shr PageShift)
|
||||||
|
if t != nil:
|
||||||
|
u = (u %% PageSize) /% MemAlign
|
||||||
|
result = (t.bits[u shr IntShift] and (1 shl (u and IntMask))) != 0
|
||||||
|
else:
|
||||||
|
result = false
|
||||||
|
|
||||||
|
proc incl(s: var TCellSet, cell: PCell) {.noinline.} =
|
||||||
|
var u = cast[TAddress](cell)
|
||||||
|
var t = CellSetPut(s, u shr PageShift)
|
||||||
|
u = (u %% PageSize) /% MemAlign
|
||||||
|
t.bits[u shr IntShift] = t.bits[u shr IntShift] or (1 shl (u and IntMask))
|
||||||
|
|
||||||
|
proc excl(s: var TCellSet, cell: PCell) =
|
||||||
|
var u = cast[TAddress](cell)
|
||||||
|
var t = CellSetGet(s, u shr PageShift)
|
||||||
|
if t != nil:
|
||||||
|
u = (u %% PageSize) /% MemAlign
|
||||||
|
t.bits[u shr IntShift] = (t.bits[u shr IntShift] and
|
||||||
|
not (1 shl (u and IntMask)))
|
||||||
|
|
||||||
|
proc containsOrIncl(s: var TCellSet, cell: PCell): bool =
|
||||||
|
var u = cast[TAddress](cell)
|
||||||
|
var t = CellSetGet(s, u shr PageShift)
|
||||||
|
if t != nil:
|
||||||
|
u = (u %% PageSize) /% MemAlign
|
||||||
|
result = (t.bits[u shr IntShift] and (1 shl (u and IntMask))) != 0
|
||||||
|
if not result:
|
||||||
|
t.bits[u shr IntShift] = t.bits[u shr IntShift] or
|
||||||
|
(1 shl (u and IntMask))
|
||||||
|
else:
|
||||||
|
Incl(s, cell)
|
||||||
|
result = false
|
||||||
|
|
||||||
|
iterator elements(t: TCellSet): PCell {.inline.} =
|
||||||
|
# while traversing it is forbidden to add pointers to the tree!
|
||||||
|
var r = t.head
|
||||||
|
while r != nil:
|
||||||
|
var i = 0
|
||||||
|
while i <= high(r.bits):
|
||||||
|
var w = r.bits[i] # taking a copy of r.bits[i] here is correct, because
|
||||||
|
# modifying operations are not allowed during traversation
|
||||||
|
var j = 0
|
||||||
|
while w != 0: # test all remaining bits for zero
|
||||||
|
if (w and 1) != 0: # the bit is set!
|
||||||
|
yield cast[PCell]((r.key shl PageShift) or
|
||||||
|
(i shl IntShift +% j) *% MemAlign)
|
||||||
|
inc(j)
|
||||||
|
w = w shr 1
|
||||||
|
inc(i)
|
||||||
|
r = r.next
|
||||||
|
|
||||||
|
|
@ -23,6 +23,7 @@ const
|
||||||
reallyOsDealloc = true
|
reallyOsDealloc = true
|
||||||
coalescRight = true
|
coalescRight = true
|
||||||
coalescLeft = true
|
coalescLeft = true
|
||||||
|
overwriteFree = false
|
||||||
|
|
||||||
type
|
type
|
||||||
PPointer = ptr pointer
|
PPointer = ptr pointer
|
||||||
|
|
|
||||||
|
|
@ -358,7 +358,8 @@ begin
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
g.len := pos - g.pos;
|
g.len := pos - g.pos;
|
||||||
if (g.kind <> gtEof) and (g.len <= 0) then InternalError('nimNextToken');
|
if (g.kind <> gtEof) and (g.len <= 0) then
|
||||||
|
InternalError('nimNextToken: ' + toString(g.buf));
|
||||||
g.pos := pos;
|
g.pos := pos;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// The Nimrod Compiler
|
// The Nimrod Compiler
|
||||||
// (c) Copyright 2008 Andreas Rumpf
|
// (c) Copyright 2009 Andreas Rumpf
|
||||||
//
|
//
|
||||||
// See the file "copying.txt", included in this
|
// See the file "copying.txt", included in this
|
||||||
// distribution, for details about the copyright.
|
// distribution, for details about the copyright.
|
||||||
|
|
@ -45,6 +45,7 @@ function toString(i: BiggestInt): string; overload;
|
||||||
//function toString(i: int): string; overload;
|
//function toString(i: int): string; overload;
|
||||||
function ToStringF(const r: Real): string; overload;
|
function ToStringF(const r: Real): string; overload;
|
||||||
function ToString(b: Boolean): string; overload;
|
function ToString(b: Boolean): string; overload;
|
||||||
|
function ToString(b: PChar): string; overload;
|
||||||
|
|
||||||
function IntToStr(i: BiggestInt; minChars: int): string;
|
function IntToStr(i: BiggestInt; minChars: int): string;
|
||||||
|
|
||||||
|
|
@ -415,6 +416,11 @@ begin
|
||||||
result := sysUtils.intToStr(i);
|
result := sysUtils.intToStr(i);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function ToString(b: PChar): string; overload;
|
||||||
|
begin
|
||||||
|
result := string(b);
|
||||||
|
end;
|
||||||
|
|
||||||
function normalize(const s: string): string;
|
function normalize(const s: string): string;
|
||||||
var
|
var
|
||||||
i: int;
|
i: int;
|
||||||
|
|
|
||||||
103
pycompab.py
Normal file
103
pycompab.py
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
""" Python compability library
|
||||||
|
|
||||||
|
With careful and painful coding, compability from Python 1.5.2 up to 3.0
|
||||||
|
is achieved. Don't try this at home.
|
||||||
|
|
||||||
|
Copyright 2009, Andreas Rumpf
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
python3 = sys.version[0] >= "3"
|
||||||
|
python26 = sys.version[:3] == "2.6"
|
||||||
|
|
||||||
|
true, false = 0==0, 0==1
|
||||||
|
|
||||||
|
if python3:
|
||||||
|
sys.exit("This script does not yet work with Python 3.0")
|
||||||
|
|
||||||
|
try:
|
||||||
|
from cStringIO import StringIO
|
||||||
|
except ImportError:
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
if python3:
|
||||||
|
def replace(s, a, b): return s.replace(a, b)
|
||||||
|
def lower(s): return s.lower()
|
||||||
|
def join(a, s=""): return s.join(a)
|
||||||
|
def find(s, a): return s.find(a)
|
||||||
|
def split(s, a=None): return s.split(a)
|
||||||
|
def strip(s): return s.strip()
|
||||||
|
|
||||||
|
def has_key(dic, key): return key in dic
|
||||||
|
else:
|
||||||
|
from string import replace, lower, join, find, split, strip
|
||||||
|
|
||||||
|
def has_key(dic, key): return dic.has_key(key)
|
||||||
|
|
||||||
|
if not python3 and not python26:
|
||||||
|
import md5
|
||||||
|
def newMD5(): return md5.new()
|
||||||
|
def MD5update(obj, x):
|
||||||
|
return obj.update(x)
|
||||||
|
else:
|
||||||
|
import hashlib
|
||||||
|
def newMD5(): return hashlib.md5()
|
||||||
|
def MD5update(obj, x):
|
||||||
|
if python26:
|
||||||
|
return obj.update(x)
|
||||||
|
else:
|
||||||
|
return obj.update(bytes(x, "utf-8"))
|
||||||
|
|
||||||
|
def mydigest(hasher):
|
||||||
|
result = ""
|
||||||
|
for c in hasher.digest():
|
||||||
|
if python3:
|
||||||
|
x = hex(c)[2:]
|
||||||
|
else:
|
||||||
|
x = hex(ord(c))[2:]
|
||||||
|
if len(x) == 1: x = "0" + x
|
||||||
|
result = result + x
|
||||||
|
return result
|
||||||
|
|
||||||
|
def Subs(frmt, *args, **substitution):
|
||||||
|
DIGITS = "0123456789"
|
||||||
|
LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
chars = DIGITS+LETTERS+"_"
|
||||||
|
d = substitution
|
||||||
|
a = args
|
||||||
|
result = []
|
||||||
|
i = 0
|
||||||
|
L = len(frmt)
|
||||||
|
while i < L:
|
||||||
|
if frmt[i] == '$':
|
||||||
|
i = i+1
|
||||||
|
if frmt[i] == '$':
|
||||||
|
result.append('$')
|
||||||
|
i = i+1
|
||||||
|
elif frmt[i] == '{':
|
||||||
|
i = i+1
|
||||||
|
j = i
|
||||||
|
while frmt[i] != '}': i = i+1
|
||||||
|
i = i+1 # skip }
|
||||||
|
x = frmt[j:i-1]
|
||||||
|
if x[0] in DIGITS:
|
||||||
|
result.append(str(a[int(x)-1]))
|
||||||
|
else:
|
||||||
|
result.append(str(d[x]))
|
||||||
|
elif frmt[i] in chars:
|
||||||
|
j = i
|
||||||
|
i = i+1
|
||||||
|
while i < len(frmt) and frmt[i] in chars: i = i + 1
|
||||||
|
x = frmt[j:i]
|
||||||
|
if x[0] in DIGITS:
|
||||||
|
result.append(str(a[int(x)-1]))
|
||||||
|
else:
|
||||||
|
result.append(str(d[x]))
|
||||||
|
else:
|
||||||
|
assert(false)
|
||||||
|
else:
|
||||||
|
result.append(frmt[i])
|
||||||
|
i = i+1
|
||||||
|
return join(result, "")
|
||||||
|
|
||||||
|
|
@ -5,12 +5,7 @@
|
||||||
@if llvm_gcc or gcc:
|
@if llvm_gcc or gcc:
|
||||||
# GCC, LLVM and Visual C++ have a problem to optimize some modules.
|
# GCC, LLVM and Visual C++ have a problem to optimize some modules.
|
||||||
# This is really strange.
|
# This is really strange.
|
||||||
@if windows or macosX:
|
cgen.speed = "-O0"
|
||||||
cgen.speed = "-O0"
|
|
||||||
@else:
|
|
||||||
cgen.speed = "-O1 -fno-strict-aliasing"
|
|
||||||
@end
|
|
||||||
@elif vcc:
|
@elif vcc:
|
||||||
cgen.speed = ""
|
cgen.speed = ""
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue