right shift is now by default sign preserving (#11322)

* right shift is now by default sign preserving
* fix hashString and semfold
* enable arithmetic shift right globally for CI
* fix typo
* remove xxx
* use oldShiftRight as flag
* apply feedback
* add changelog entry
This commit is contained in:
Arne Döring 2019-05-29 16:48:00 +02:00 • committed by Andreas Rumpf
commit 88b5dd3362
18 changed files with 211 additions and 187 deletions

View file

@ -23,7 +23,7 @@ import
hashes, math
type
BitScalar = int
BitScalar = uint
const
InitIntSetSize = 8 # must be a power of two!
@ -102,8 +102,8 @@ proc intSetPut(t: var IntSet, key: int): PTrunk =
proc bitincl(s: var IntSet, key: int) {.inline.} =
var t = intSetPut(s, `shr`(key, TrunkShift))
var u = key and TrunkMask
t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] or
`shl`(1, u and IntMask)
t.bits[u shr IntShift] = t.bits[u shr IntShift] or
(BitScalar(1) shl (u and IntMask))
proc exclImpl(s: var IntSet, key: int) =
if s.elems <= s.a.len:
@ -113,11 +113,11 @@ proc exclImpl(s: var IntSet, key: int) =
dec s.elems
return
else:
var t = intSetGet(s, `shr`(key, TrunkShift))
var t = intSetGet(s, key shr TrunkShift)
if t != nil:
var u = key and TrunkMask
t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] and
not `shl`(1, u and IntMask)
t.bits[u shr IntShift] = t.bits[u shr IntShift] and
not(BitScalar(1) shl (u and IntMask))
template dollarImpl(): untyped =
result = "{"
@ -137,7 +137,7 @@ iterator items*(s: IntSet): int {.inline.} =
while r != nil:
var i = 0
while i <= high(r.bits):
var w = r.bits[i]
var w: uint = r.bits[i]
# taking a copy of r.bits[i] here is correct, because
# modifying operations are not allowed during traversation
var j = 0
@ -186,7 +186,7 @@ proc contains*(s: IntSet, key: int): bool =
var t = intSetGet(s, `shr`(key, TrunkShift))
if t != nil:
var u = key and TrunkMask
result = (t.bits[`shr`(u, IntShift)] and `shl`(1, u and IntMask)) != 0
result = (t.bits[u shr IntShift] and (BitScalar(1) shl (u and IntMask))) != 0
else:
result = false
@ -268,10 +268,10 @@ proc containsOrIncl*(s: var IntSet, key: int): bool =
var t = intSetGet(s, `shr`(key, TrunkShift))
if t != nil:
var u = key and TrunkMask
result = (t.bits[`shr`(u, IntShift)] and `shl`(1, u and IntMask)) != 0
result = (t.bits[u shr IntShift] and BitScalar(1) shl (u and IntMask)) != 0
if not result:
t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] or
`shl`(1, u and IntMask)
t.bits[u shr IntShift] = t.bits[u shr IntShift] or
(BitScalar(1) shl (u and IntMask))
else:
incl(s, key)
result = false

View file

@ -60,17 +60,22 @@ proc `!&`*(h: Hash, val: int): Hash {.inline.} =
## Mixes a hash value `h` with `val` to produce a new hash value.
##
## This is only needed if you need to implement a hash proc for a new datatype.
result = h +% val
result = result +% result shl 10
result = result xor (result shr 6)
let h = cast[uint](h)
let val = cast[uint](val)
var res = h + val
res = res + res shl 10
res = res xor (res shr 6)
result = cast[Hash](res)
proc `!$`*(h: Hash): Hash {.inline.} =
## Finishes the computation of the hash value.
##
## This is only needed if you need to implement a hash proc for a new datatype.
result = h +% h shl 3
result = result xor (result shr 11)
result = result +% result shl 15
let h = cast[uint](h) # Hash is practically unsigned.
var res = h + h shl 3
res = res xor (res shr 11)
res = res + res shl 15
result = cast[Hash](res)
proc hashData*(data: pointer, size: int): Hash =
## Hashes an array of bytes of size `size`.
@ -105,7 +110,7 @@ proc hash*(x: pointer): Hash {.inline.} =
}
"""
else:
result = (cast[Hash](x)) shr 3 # skip the alignment
result = cast[Hash](cast[uint](x) shr 3) # skip the alignment
when not defined(booting):
proc hash*[T: proc](x: T): Hash {.inline.} =

View file

@ -48,12 +48,12 @@ proc runeLen*(s: string): int {.rtl, extern: "nuc$1".} =
var i = 0
while i < len(s):
if ord(s[i]) <=% 127: inc(i)
elif ord(s[i]) shr 5 == 0b110: inc(i, 2)
elif ord(s[i]) shr 4 == 0b1110: inc(i, 3)
elif ord(s[i]) shr 3 == 0b11110: inc(i, 4)
elif ord(s[i]) shr 2 == 0b111110: inc(i, 5)
elif ord(s[i]) shr 1 == 0b1111110: inc(i, 6)
if uint(s[i]) <= 127: inc(i)
elif uint(s[i]) shr 5 == 0b110: inc(i, 2)
elif uint(s[i]) shr 4 == 0b1110: inc(i, 3)
elif uint(s[i]) shr 3 == 0b11110: inc(i, 4)
elif uint(s[i]) shr 2 == 0b111110: inc(i, 5)
elif uint(s[i]) shr 1 == 0b1111110: inc(i, 6)
else: inc i
inc(result)
@ -67,12 +67,12 @@ proc runeLenAt*(s: string, i: Natural): int =
doAssert a.runeLenAt(0) == 1
doAssert a.runeLenAt(1) == 2
if ord(s[i]) <=% 127: result = 1
elif ord(s[i]) shr 5 == 0b110: result = 2
elif ord(s[i]) shr 4 == 0b1110: result = 3
elif ord(s[i]) shr 3 == 0b11110: result = 4
elif ord(s[i]) shr 2 == 0b111110: result = 5
elif ord(s[i]) shr 1 == 0b1111110: result = 6
if uint(s[i]) <= 127: result = 1
elif uint(s[i]) shr 5 == 0b110: result = 2
elif uint(s[i]) shr 4 == 0b1110: result = 3
elif uint(s[i]) shr 3 == 0b11110: result = 4
elif uint(s[i]) shr 2 == 0b111110: result = 5
elif uint(s[i]) shr 1 == 0b1111110: result = 6
else: result = 1
const replRune = Rune(0xFFFD)
@ -83,76 +83,76 @@ template fastRuneAt*(s: string, i: int, result: untyped, doInc = true) =
## If ``doInc == true`` (default), ``i`` is incremented by the number
## of bytes that have been processed.
bind ones
if ord(s[i]) <=% 127:
result = Rune(ord(s[i]))
if uint(s[i]) <= 127:
result = Rune(uint(s[i]))
when doInc: inc(i)
elif ord(s[i]) shr 5 == 0b110:
# assert(ord(s[i+1]) shr 6 == 0b10)
elif uint(s[i]) shr 5 == 0b110:
# assert(uint(s[i+1]) shr 6 == 0b10)
if i <= s.len - 2:
result = Rune((ord(s[i]) and (ones(5))) shl 6 or
(ord(s[i+1]) and ones(6)))
result = Rune((uint(s[i]) and (ones(5))) shl 6 or
(uint(s[i+1]) and ones(6)))
when doInc: inc(i, 2)
else:
result = replRune
when doInc: inc(i)
elif ord(s[i]) shr 4 == 0b1110:
# assert(ord(s[i+1]) shr 6 == 0b10)
# assert(ord(s[i+2]) shr 6 == 0b10)
elif uint(s[i]) shr 4 == 0b1110:
# assert(uint(s[i+1]) shr 6 == 0b10)
# assert(uint(s[i+2]) shr 6 == 0b10)
if i <= s.len - 3:
result = Rune((ord(s[i]) and ones(4)) shl 12 or
(ord(s[i+1]) and ones(6)) shl 6 or
(ord(s[i+2]) and ones(6)))
result = Rune((uint(s[i]) and ones(4)) shl 12 or
(uint(s[i+1]) and ones(6)) shl 6 or
(uint(s[i+2]) and ones(6)))
when doInc: inc(i, 3)
else:
result = replRune
when doInc: inc(i)
elif ord(s[i]) shr 3 == 0b11110:
# assert(ord(s[i+1]) shr 6 == 0b10)
# assert(ord(s[i+2]) shr 6 == 0b10)
# assert(ord(s[i+3]) shr 6 == 0b10)
elif uint(s[i]) shr 3 == 0b11110:
# assert(uint(s[i+1]) shr 6 == 0b10)
# assert(uint(s[i+2]) shr 6 == 0b10)
# assert(uint(s[i+3]) shr 6 == 0b10)
if i <= s.len - 4:
result = Rune((ord(s[i]) and ones(3)) shl 18 or
(ord(s[i+1]) and ones(6)) shl 12 or
(ord(s[i+2]) and ones(6)) shl 6 or
(ord(s[i+3]) and ones(6)))
result = Rune((uint(s[i]) and ones(3)) shl 18 or
(uint(s[i+1]) and ones(6)) shl 12 or
(uint(s[i+2]) and ones(6)) shl 6 or
(uint(s[i+3]) and ones(6)))
when doInc: inc(i, 4)
else:
result = replRune
when doInc: inc(i)
elif ord(s[i]) shr 2 == 0b111110:
# assert(ord(s[i+1]) shr 6 == 0b10)
# assert(ord(s[i+2]) shr 6 == 0b10)
# assert(ord(s[i+3]) shr 6 == 0b10)
# assert(ord(s[i+4]) shr 6 == 0b10)
elif uint(s[i]) shr 2 == 0b111110:
# assert(uint(s[i+1]) shr 6 == 0b10)
# assert(uint(s[i+2]) shr 6 == 0b10)
# assert(uint(s[i+3]) shr 6 == 0b10)
# assert(uint(s[i+4]) shr 6 == 0b10)
if i <= s.len - 5:
result = Rune((ord(s[i]) and ones(2)) shl 24 or
(ord(s[i+1]) and ones(6)) shl 18 or
(ord(s[i+2]) and ones(6)) shl 12 or
(ord(s[i+3]) and ones(6)) shl 6 or
(ord(s[i+4]) and ones(6)))
result = Rune((uint(s[i]) and ones(2)) shl 24 or
(uint(s[i+1]) and ones(6)) shl 18 or
(uint(s[i+2]) and ones(6)) shl 12 or
(uint(s[i+3]) and ones(6)) shl 6 or
(uint(s[i+4]) and ones(6)))
when doInc: inc(i, 5)
else:
result = replRune
when doInc: inc(i)
elif ord(s[i]) shr 1 == 0b1111110:
# assert(ord(s[i+1]) shr 6 == 0b10)
# assert(ord(s[i+2]) shr 6 == 0b10)
# assert(ord(s[i+3]) shr 6 == 0b10)
# assert(ord(s[i+4]) shr 6 == 0b10)
# assert(ord(s[i+5]) shr 6 == 0b10)
elif uint(s[i]) shr 1 == 0b1111110:
# assert(uint(s[i+1]) shr 6 == 0b10)
# assert(uint(s[i+2]) shr 6 == 0b10)
# assert(uint(s[i+3]) shr 6 == 0b10)
# assert(uint(s[i+4]) shr 6 == 0b10)
# assert(uint(s[i+5]) shr 6 == 0b10)
if i <= s.len - 6:
result = Rune((ord(s[i]) and ones(1)) shl 30 or
(ord(s[i+1]) and ones(6)) shl 24 or
(ord(s[i+2]) and ones(6)) shl 18 or
(ord(s[i+3]) and ones(6)) shl 12 or
(ord(s[i+4]) and ones(6)) shl 6 or
(ord(s[i+5]) and ones(6)))
result = Rune((uint(s[i]) and ones(1)) shl 30 or
(uint(s[i+1]) and ones(6)) shl 24 or
(uint(s[i+2]) and ones(6)) shl 18 or
(uint(s[i+3]) and ones(6)) shl 12 or
(uint(s[i+4]) and ones(6)) shl 6 or
(uint(s[i+5]) and ones(6)))
when doInc: inc(i, 6)
else:
result = replRune
when doInc: inc(i)
else:
result = Rune(ord(s[i]))
result = Rune(uint(s[i]))
when doInc: inc(i)
proc runeAt*(s: string, i: Natural): Rune =
@ -180,20 +180,20 @@ proc validateUTF8*(s: string): int =
var i = 0
let L = s.len
while i < L:
if ord(s[i]) <=% 127:
if uint(s[i]) <= 127:
inc(i)
elif ord(s[i]) shr 5 == 0b110:
if ord(s[i]) < 0xc2: return i # Catch overlong ascii representations.
if i+1 < L and ord(s[i+1]) shr 6 == 0b10: inc(i, 2)
elif uint(s[i]) shr 5 == 0b110:
if uint(s[i]) < 0xc2: return i # Catch overlong ascii representations.
if i+1 < L and uint(s[i+1]) shr 6 == 0b10: inc(i, 2)
else: return i
elif ord(s[i]) shr 4 == 0b1110:
if i+2 < L and ord(s[i+1]) shr 6 == 0b10 and ord(s[i+2]) shr 6 == 0b10:
elif uint(s[i]) shr 4 == 0b1110:
if i+2 < L and uint(s[i+1]) shr 6 == 0b10 and uint(s[i+2]) shr 6 == 0b10:
inc i, 3
else: return i
elif ord(s[i]) shr 3 == 0b11110:
if i+3 < L and ord(s[i+1]) shr 6 == 0b10 and
ord(s[i+2]) shr 6 == 0b10 and
ord(s[i+3]) shr 6 == 0b10:
elif uint(s[i]) shr 3 == 0b11110:
if i+3 < L and uint(s[i+1]) shr 6 == 0b10 and
uint(s[i+2]) shr 6 == 0b10 and
uint(s[i+3]) shr 6 == 0b10:
inc i, 4
else: return i
else:
@ -906,7 +906,7 @@ proc lastRune*(s: string; last: int): (Rune, int) =
result = (Rune(s[last]), 1)
else:
var L = 0
while last-L >= 0 and ord(s[last-L]) shr 6 == 0b10: inc(L)
while last-L >= 0 and uint(s[last-L]) shr 6 == 0b10: inc(L)
var r: Rune
fastRuneAt(s, last-L, r, false)
result = (r, L+1)

View file

@ -1270,24 +1270,34 @@ else:
proc `mod`*(x, y: int64): int64 {.magic: "ModI64", noSideEffect.}
when defined(nimNewShiftOps):
proc `shr`*(x: int, y: SomeInteger): int {.magic: "ShrI", noSideEffect.}
## Computes the `shift right` operation of `x` and `y`, filling
## vacant bit positions with zeros.
##
## **Note**: `Operator precedence <manual.html#syntax-precedence>`_
## is different than in *C*.
##
## See also:
## * `ashr proc <#ashr,int,SomeInteger>`_ for arithmetic shift right
##
## .. code-block:: Nim
## 0b0001_0000'i8 shr 2 == 0b0000_0100'i8
## 0b1000_0000'i8 shr 8 == 0b0000_0000'i8
## 0b0000_0001'i8 shr 1 == 0b0000_0000'i8
proc `shr`*(x: int8, y: SomeInteger): int8 {.magic: "ShrI", noSideEffect.}
proc `shr`*(x: int16, y: SomeInteger): int16 {.magic: "ShrI", noSideEffect.}
proc `shr`*(x: int32, y: SomeInteger): int32 {.magic: "ShrI", noSideEffect.}
proc `shr`*(x: int64, y: SomeInteger): int64 {.magic: "ShrI", noSideEffect.}
when defined(oldShiftRight) or not defined(nimAshr):
const shrDepMessage = "`shr` will become sign preserving."
proc `shr`*(x: int, y: SomeInteger): int {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
proc `shr`*(x: int8, y: SomeInteger): int8 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
proc `shr`*(x: int16, y: SomeInteger): int16 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
proc `shr`*(x: int32, y: SomeInteger): int32 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
proc `shr`*(x: int64, y: SomeInteger): int64 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
else:
proc `shr`*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.}
## Computes the `shift right` operation of `x` and `y`, filling
## vacant bit positions with the sign bit.
##
## **Note**: `Operator precedence <manual.html#syntax-precedence>`_
## is different than in *C*.
##
## See also:
## * `ashr proc <#ashr,int,SomeInteger>`_ for arithmetic shift right
##
## .. code-block:: Nim
## 0b0001_0000'i8 shr 2 == 0b0000_0100'i8
## 0b1000_0000'i8 shr 8 == 0b0000_0000'i8
## 0b0000_0001'i8 shr 1 == 0b0000_0000'i8
proc `shr`*(x: int8, y: SomeInteger): int8 {.magic: "AshrI", noSideEffect.}
proc `shr`*(x: int16, y: SomeInteger): int16 {.magic: "AshrI", noSideEffect.}
proc `shr`*(x: int32, y: SomeInteger): int32 {.magic: "AshrI", noSideEffect.}
proc `shr`*(x: int64, y: SomeInteger): int64 {.magic: "AshrI", noSideEffect.}
proc `shl`*(x: int, y: SomeInteger): int {.magic: "ShlI", noSideEffect.}
## Computes the `shift left` operation of `x` and `y`.

View file

@ -38,7 +38,7 @@ type
Trunk = object
next: PTrunk # all nodes are connected with this pointer
key: int # start address at bit 0
bits: array[0..IntsPerTrunk-1, int] # a bit vector
bits: array[0..IntsPerTrunk-1, uint] # a bit vector
TrunkBuckets = array[0..255, PTrunk]
IntSet = object
@ -332,21 +332,21 @@ proc contains(s: IntSet, key: int): bool =
var t = intSetGet(s, key shr TrunkShift)
if t != nil:
var u = key and TrunkMask
result = (t.bits[u shr IntShift] and (1 shl (u and IntMask))) != 0
result = (t.bits[u shr IntShift] and (uint(1) shl (u and IntMask))) != 0
else:
result = false
proc incl(a: var MemRegion, s: var IntSet, key: int) =
var t = intSetPut(a, s, key shr TrunkShift)
var u = key and TrunkMask
t.bits[u shr IntShift] = t.bits[u shr IntShift] or (1 shl (u and IntMask))
t.bits[u shr IntShift] = t.bits[u shr IntShift] or (uint(1) shl (u and IntMask))
proc excl(s: var IntSet, key: int) =
var t = intSetGet(s, key shr TrunkShift)
if t != nil:
var u = key and TrunkMask
t.bits[u shr IntShift] = t.bits[u shr IntShift] and not
(1 shl (u and IntMask))
(uint(1) shl (u and IntMask))
iterator elements(t: IntSet): int {.inline.} =
# while traversing it is forbidden to change the set!

View file

@ -27,7 +27,7 @@ type
BitIndex = range[0..UnitsPerPage-1]
PageDesc {.final, pure.} = object
next: PPageDesc # all nodes are connected with this pointer
key: ByteAddress # start address at bit 0
key: uint # start address at bit 0
bits: array[BitIndex, int] # a bit vector
PPageDescArray = ptr UncheckedArray[PPageDesc]
@ -97,7 +97,7 @@ proc nextTry(h, maxHash: int): int {.inline.} =
# generates each int in range(maxHash) exactly once (see any text on
# random-number generation for proof).
proc cellSetGet(t: CellSet, key: ByteAddress): PPageDesc =
proc cellSetGet(t: CellSet, key: uint): PPageDesc =
var h = cast[int](key) and t.max
while t.data[h] != nil:
if t.data[h].key == key: return t.data[h]
@ -122,7 +122,7 @@ proc cellSetEnlarge(t: var CellSet) =
dealloc(t.data)
t.data = n
proc cellSetPut(t: var CellSet, key: ByteAddress): PPageDesc =
proc cellSetPut(t: var CellSet, key: uint): PPageDesc =
var h = cast[int](key) and t.max
while true:
var x = t.data[h]
@ -146,33 +146,33 @@ proc cellSetPut(t: var CellSet, key: ByteAddress): PPageDesc =
# ---------- slightly higher level procs --------------------------------------
proc contains(s: CellSet, cell: PCell): bool =
var u = cast[ByteAddress](cell)
var u = cast[uint](cell)
var t = cellSetGet(s, u shr PageShift)
if t != nil:
u = (u %% PageSize) /% MemAlign
u = (u mod PageSize) div MemAlign
result = (t.bits[u shr IntShift] and (1 shl (u and IntMask))) != 0
else:
result = false
proc incl(s: var CellSet, cell: PCell) {.noinline.} =
var u = cast[ByteAddress](cell)
var u = cast[uint](cell)
var t = cellSetPut(s, u shr PageShift)
u = (u %% PageSize) /% MemAlign
u = (u mod PageSize) div MemAlign
t.bits[u shr IntShift] = t.bits[u shr IntShift] or (1 shl (u and IntMask))
proc excl(s: var CellSet, cell: PCell) =
var u = cast[ByteAddress](cell)
var u = cast[uint](cell)
var t = cellSetGet(s, u shr PageShift)
if t != nil:
u = (u %% PageSize) /% MemAlign
u = (u mod PageSize) div MemAlign
t.bits[u shr IntShift] = (t.bits[u shr IntShift] and
not (1 shl (u and IntMask)))
proc containsOrIncl(s: var CellSet, cell: PCell): bool =
var u = cast[ByteAddress](cell)
var u = cast[uint](cell)
var t = cellSetGet(s, u shr PageShift)
if t != nil:
u = (u %% PageSize) /% MemAlign
u = (u mod PageSize) div 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
@ -185,15 +185,15 @@ iterator elements(t: CellSet): 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 i: uint = 0
while int(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
var j: uint = 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)
(i shl IntShift + j) * MemAlign)
inc(j)
w = w shr 1
inc(i)
@ -238,16 +238,16 @@ iterator elementsExcept(t, s: CellSet): PCell {.inline.} =
var r = t.head
while r != nil:
let ss = cellSetGet(s, r.key)
var i = 0
while i <= high(r.bits):
var i:uint = 0
while int(i) <= high(r.bits):
var w = r.bits[i]
if ss != nil:
w = w and not ss.bits[i]
var j = 0
var j:uint = 0
while w != 0:
if (w and 1) != 0:
yield cast[PCell]((r.key shl PageShift) or
(i shl IntShift +% j) *% MemAlign)
(i shl IntShift + j) * MemAlign)
inc(j)
w = w shr 1
inc(i)

View file

@ -133,7 +133,7 @@ proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
result = usrToCell(c).typ
proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
result = int(usrToCell(p).refcount) shr rcShift
result = usrToCell(p).refcount shr rcShift
# this that has to equals zero, otherwise we have to round up UnitsPerPage:
when BitsPerPage mod (sizeof(int)*8) != 0:

View file

@ -30,15 +30,15 @@ proc eqStrings(a, b: string): bool {.inline, compilerProc.} =
proc hashString(s: string): int {.compilerproc.} =
# the compiler needs exactly the same hash function!
# this used to be used for efficient generation of string case statements
var h = 0
var h : uint = 0
for i in 0..len(s)-1:
h = h +% ord(s[i])
h = h +% h shl 10
h = h + uint(s[i])
h = h + h shl 10
h = h xor (h shr 6)
h = h +% h shl 3
h = h + h shl 3
h = h xor (h shr 11)
h = h +% h shl 15
result = h
h = h + h shl 15
result = cast[int](h)
proc addInt*(result: var string; x: int64) =
## Converts integer to its string representation and appends it to `result`.