이름 하나당 사본 하나와 그것을 대신하는 숫자. 컴파일러는 이름을 끊임없이
비교하고 사방에 저장하는데, StrId 둘을 비교하는 것은 정수 둘을 비교하는
것이고 하나를 저장하는 것은 4바이트에 소유권 없음이다.
str 을 꺼내는 API 를 일부러 두지 않았다. 텍스트를 빌리는 것은 interner 를
빌리는 것인데, interner 는 이름을 든 채로 계속 더 넣고 싶은 바로 그 물건이다
-- 파서는 식별자를 읽으면서 같은 숨에 다음 것을 등록한다. 텍스트를 열어서
하려던 일은 전부 여기 있다: eq, len_of, hash_of, find, copy_into.
그리고 이것이 제네릭 인스턴스를 필드로 담는 구조체를 통째로 깨뜨리던 버그를
드러냈다.
Holder{ bytes: ^[]mut u8, used: usize, seen: map.Map(u32) }
36 바이트여야 하는데 12 로 잡혔다.
Map(u32) 를 짓는 중에 그 안의 Slot(u32) 를 인스턴스화하면 거기서 배치 패스가
다시 돈다. 그때 Map(u32) 는 field_count 는 4 인데 필드 배열이 아직 아무것도
말하지 않는 상태라, 크기 0 으로 확정되고 굳었다. 이미 크기가 있는 타입은
아무도 다시 계산하지 않으니 Holder 는 그 0 을 읽었다.
셋을 고쳤다: 짓는 중인 인스턴스는 building 을 세워 배치를 거절하고, 멤버가
아직 자리를 못 잡은 집합 타입은 틀린 답으로 굳느니 물러나며, 배치 패스는
움직임이 없을 때까지 돈다.
245/245, 38/38.
63 lines
2.0 KiB
Plaintext
63 lines
2.0 KiB
Plaintext
// EXIT:0
|
|
// OUTPUT:ids 0 1 2 count 3
|
|
// OUTPUT:again 0 same yes count 3
|
|
// OUTPUT:eq yes no len 4
|
|
// OUTPUT:find 1 missing yes
|
|
// OUTPUT:hash steady yes apart yes
|
|
// OUTPUT:copied unit 4
|
|
// OUTPUT:balanced
|
|
unit interns;
|
|
|
|
import std.io;
|
|
import std.sys;
|
|
import std.intern;
|
|
|
|
// One copy of every distinct name, and a number that stands for it. Comparing
|
|
// two names is comparing two integers; storing one costs four bytes and no
|
|
// ownership.
|
|
//
|
|
// There is deliberately no way to get a `str` back out: a borrow of the text
|
|
// would be a borrow of the interner, and the interner is exactly what a parser
|
|
// wants to keep adding to while it holds names.
|
|
|
|
fn run() -> !void {
|
|
var t: intern.Interner = try intern.Interner.with_capacity(8);
|
|
let a: intern.StrId = try t.intern("unit");
|
|
let b: intern.StrId = try t.intern("fn");
|
|
let c: intern.StrId = try t.intern("struct");
|
|
@print("ids {} {} {} count {}\n", a.raw, b.raw, c.raw, t.count_of());
|
|
|
|
// The same name twice is the same number, and costs nothing new.
|
|
let again: intern.StrId = try t.intern("unit");
|
|
@print("again {} same {} count {}\n", again.raw, yesno(a.same(again)),
|
|
t.count_of());
|
|
|
|
@print("eq {} {} len {}\n", yesno(t.eq(a, "unit")), yesno(t.eq(a, "fn")),
|
|
t.len_of(a));
|
|
|
|
@print("find {} missing {}\n", t.find("fn"),
|
|
yesno(t.find("nope") == intern.NONE));
|
|
|
|
// A name hashes the same every time, and two names do not collide here.
|
|
@print("hash steady {} apart {}\n", yesno(t.hash_of(a) == t.hash_of(again)),
|
|
yesno(t.hash_of(a) != t.hash_of(b)));
|
|
|
|
// The only way to see the text: copy it somewhere you own.
|
|
var buf: [8]u8 = undefined;
|
|
let n: usize = t.copy_into(a, buf[..]);
|
|
@print("copied {} {}\n", buf[0..n], n);
|
|
return;
|
|
}
|
|
|
|
fn yesno(b: bool) -> []u8 {
|
|
if b { return "yes"; }
|
|
return "no";
|
|
}
|
|
|
|
fn main() -> i32 {
|
|
run() catch |e| { @print("failed\n"); return 1; };
|
|
if sys.allocs() == sys.frees() { @print("balanced\n"); }
|
|
else { @print("leaked\n"); }
|
|
return 0;
|
|
}
|