mirror of
https://github.com/nim-lang/Nim.git
synced 2026-07-15 05:30:46 +00:00
Companion to readRawData whose pointer stays valid across moves/copies of the string. Under --strings:sso it promotes a small inline string to its heap representation; under refc/v2 the data is already heap-resident so it aliases readRawData. Uniform `var string` signature on every backend so code can prepare for --strings:sso without `when declared`.
95 lines
3.7 KiB
Nim
95 lines
3.7 KiB
Nim
discard """
|
|
matrix: "--mm:refc; --mm:orc; --mm:orc --strings:sso; --backend:cpp --mm:orc; --backend:js --mm:orc"
|
|
output: "OK"
|
|
"""
|
|
|
|
# Tests for `readRawDataStable` and the SSO static-long-string promotion path.
|
|
# `readRawDataStable` is available under every string implementation (refc / v2 /
|
|
# v3-sso / js) with the same signature, so the code below compiles unchanged on
|
|
# all backends -- the point being that users can prepare for `--strings:sso`
|
|
# without `when declared` hacks.
|
|
|
|
import std/assertions
|
|
|
|
const hasNativeSso = defined(nimsso) and
|
|
(defined(gcArc) or defined(gcAtomicArc) or defined(gcOrc) or defined(gcYrc))
|
|
|
|
type
|
|
Reader = object
|
|
buf: string
|
|
p: ptr UncheckedArray[char]
|
|
|
|
proc openFromBuffer(buf: sink string): Reader =
|
|
# `result` (and thus `buf`) is moved into the caller on return. A plain
|
|
# `readRawData` pointer into a small SSO string would dangle after that move;
|
|
# `readRawDataStable` pins the buffer to a stable address first.
|
|
result = Reader(buf: buf)
|
|
result.p = readRawDataStable(result.buf)
|
|
|
|
proc testStable() =
|
|
when not defined(js): # raw pointers are a degenerate nil no-op on the JS backend
|
|
block: # short buffer (kept inline under SSO) survives the move
|
|
var r = openFromBuffer("hello")
|
|
doAssert r.buf == "hello"
|
|
doAssert r.p[0] == 'h'
|
|
doAssert r.p[4] == 'o'
|
|
# Stable pointer == the live buffer's raw data after the move.
|
|
doAssert cast[uint](r.p) == cast[uint](readRawData(r.buf))
|
|
block: # medium buffer (len 12: inline overlay under SSO)
|
|
var r = openFromBuffer("hello world!")
|
|
doAssert r.p[11] == '!'
|
|
block: # already-long buffer: returned as-is (already heap-resident)
|
|
var r = openFromBuffer("this is a fairly long string buffer")
|
|
doAssert r.p[0] == 't'
|
|
doAssert r.p[34] == 'r'
|
|
block: # empty string: API is callable (the data pointer is implementation-defined)
|
|
var e = ""
|
|
discard readRawDataStable(e)
|
|
else:
|
|
# On JS the API exists and is callable (returns nil) so call sites are portable.
|
|
var s = "hello"
|
|
discard readRawDataStable(s)
|
|
|
|
proc testStaticLongPromotion() =
|
|
# Regression for the static-long -> heap promotion: when a string literal
|
|
# longer than the inline payload (PayloadSize = 14 under SSO) is first
|
|
# mutated, the new heap block must be filled from the full static payload,
|
|
# not from the 7-byte inline hot-prefix cache. Reading from the cache copied
|
|
# 7 valid chars and then ran off into the `more` pointer bytes -- the bug that
|
|
# corrupted .nif index files on Windows bootstrap (see Nimony tstatic_long_add).
|
|
# The assertion holds on every backend; only SSO ever risked the corruption.
|
|
var content = "(.nif27)\n(index\n" # len 16
|
|
let expected = "(.nif27)\n(index\n"
|
|
content.add 'X' # triggers static-long -> heap promotion
|
|
doAssert content.len == 17
|
|
doAssert content == expected & "X"
|
|
for i in 0 ..< expected.len:
|
|
doAssert content[i] == expected[i]
|
|
|
|
when hasNativeSso:
|
|
# A few SSO-tier-boundary sanity checks (short / medium / long, COW, shrink).
|
|
proc testSsoTiers() =
|
|
var a = "(.nif27)\n(index\n" # static long
|
|
let b = "(.nif27)\n(index\n"
|
|
doAssert a == b
|
|
a.add 'Z'
|
|
doAssert a == "(.nif27)\n(index\nZ"
|
|
|
|
var c = "abcdefghijklmnop" # static long, len 16
|
|
var d = c # COW share
|
|
d[0] = 'X'
|
|
doAssert c == "abcdefghijklmnop" # original untouched
|
|
doAssert d == "Xbcdefghijklmnop"
|
|
|
|
var e = "abcdefghijklmnop"
|
|
e.setLen 3 # shrink below the inline cache size
|
|
doAssert e == "abc"
|
|
doAssert e.len == 3
|
|
else:
|
|
proc testSsoTiers() = discard
|
|
|
|
testStable()
|
|
testStaticLongPromotion()
|
|
testSsoTiers()
|
|
echo "OK"
|