collections: Buffer<Item> joins the List family and Bytes becomes Buffer<UInt8> #300
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
After #54,
Buffer<Item>lives instd/corewith only the traits core owns (Iterate,Length,MutableIndex,MutableSlice,From<Iterate>, plusEquals/Show/Hash).docs/design/COLLECTIONS.mdsection 3.7 saysBytesbecomesBuffer<UInt8>"whenBufferexists, and the alias is the only line that changes"; that is true only onceBufferis aListlike the others, whichstd/corecannot declare becauseListlives instd/collections. (TheCollectiontrait is gone since section 6b/6d: the family isList<Item>itself, withclearandcompactdeclared on it.)Implements: COLLECTIONS.md 3.7 (
Bytesrow of the table and the paragraph "BytesstaysList<UInt8>", which this issue supersedes), ROADMAP collections / ECS gap 8.Acceptance criteria
Commit 1,
feat(collections): Buffer is a Liststd/collectionsdeclaresextend<Item> Buffer<Item> with List<Item>.Buffer's own members (append,insert,removeAt,reverse,clear,get/at,set,update,replace,slice,keys,iterate,length) satisfy the trait; the trait defaults (sort,remove,swapAt,appended,windows,chunks,first,contains, ...) fill the rest.buffer.compact()resolves to theListdefault (no-op, see the decision comment: no native,withCapacityis a hint the runtime drops).Buffer<Int>is accepted where aList<Int>is asked (parameter, field,for, spread) and the defaults run on it: newstd/collections/tests/buffer-list.test.trband a conformance programtests/conformance/buffer-list.trb(+.expected,.allocations) cover each default with aBuffer, assertbuffer.sliceis still a copy andbuffer[1..3] = .../updatestay in place.Buffer<UInt8>/Bytesstill builds throughBuffer.from(theisBuiltThroughFrompath, as forRingList); a test pins it (const b: Buffer<UInt8> = [1, 2, 3]).benchmarks/:bytes-pass.trb(typedBuffer<UInt8>) andbytes-pass-list.trb(the same program typedList<UInt8>): a CRC-32 table loop, a copy-with-offset loop (the shape of inflate's window),appendof 4 MiB and 1000[from..to]slices of a few hundred bytes. Both are added in this commit so commit 2 changes nothing in them.Commit 2,
refactor(stream): Bytes is Buffer<UInt8>std/stream/src/bytes.trb:public type Bytes = Buffer<UInt8>, its doc comment says it is aBuffer(a copy on slice, in place on write).List<UInt8>spelled out where it meets aBytes(std/encoding,std/fs,std/compression,std/digest,std/cipher,std/signature,std/archive,std/storage,std/network,std/tls,std/http, tests,compiler/tests/language-server.test.trb) becomesBytes; the module-level natives that takeArrayList<UInt8>(fileReadBytes,networkTakeReceived,standardTakeRead,String.copyBytes, ...) takeBuffer<UInt8>(sameListStorage, soruntime/include/torb_natives.hmust not change;compiler/tests/natives.test.trbshows it).torb checkis clean onstd,tests/conformance,tests/language,docs..expected,.exit,.stderrof the programs that useBytes(file-bytes,file-streams,standard-streams,stream-take,destructor-stream,http-*,https-exchange,task-run-to-end-http) do not change, nor do the unit tests ofstd/compression,std/stream,std/encoding,std/digest. (There is nocompression-*conformance program; the compression coverage isstd/compression/tests.).allocationsbudgets: a count may rise only where aBytesslice used to share storage and now copies. Each raised budget is listed in the PR description with old and new number and the slice site that causes it; no other budget moves.bytes-passis within 10 % ofbytes-pass-list(torb time, same run, fastest of 9), and thestd/compressiondeflate+inflate round trip of 4 MiB is no more than 10 % slower at commit 2 than at commit 1. Both numbers go into the PR description. If a number fails, the implementer says which site (a hotBytesslice most likely) and fixes it with aBuffer-aware call site or stops and splits a follow-up issue; the criterion is not relaxed.Bytesparagraph, plus a short "6e. Landed" note), the doc comments ofBuffer("leaves theListvocabulary tostd/collections") andBytes,docs/PERFORMANCE.mdwhere it saysList<UInt8>is what inflate/deflate use;skills/regenerated.sh tools/gates.sh a);bis not needed (nocompiler/src/ir,backendorruntime/change).Seed
Not seed-touching: no syntax change, no native, no driver flag, and no new name the compiler looks up by string. The compiler looks up
List(ir/lower/collection.trb,wellknown.trblist) and the primitiveBuffer(ir/instantiate.trb, #299) by name, neverBytes;Bufferas aListgoes through the trait witness tables likeRingList, and a literal against it takes the existingFrom<Iterate>path. One PR, two Conventional Commits above (extension + tests first, alias flip + sweep second); both arestd/,tests/,benchmarks/,docs/only.If the first probe in commit 1 shows the checker or lowering cannot take
extend Buffer with List(a trait of another package on a native type), the implementer stops, files aloopissue for the compiler teaching (the two-commit seed rule ofCLAUDE.md), and marks this one waiting on it.Dependencies
Buffer<Item>as the heap storage primitive): landed, with #299.Notes
Bytesusers:BytesandList<UInt8>stop being the same type, andbytes[a..b]copies instead of sharing. Section 3.7 already decided it; this is a 0.x std.Buffer.compact(needs a native and a seed-taught manifest row),Window, the lint listplainContainersincompiler/src/lint/plain-values.trb.Buffer<Item>as the heap storage primitive #54Buffer<Item>as the heap storage primitive #54[loop:torbscript] claim refine by session:loop-torbscript-20261018 until 2026-10-18T00:01Z
[loop:torbscript] decided by the loop:
List<Item>itself.Collectionwas deleted in COLLECTIONS.md 6b/6d, so the issue's "or theCollectionfamily" has no second option;clearandcompactare declared onList.compactonBuffer: no native, theListdefault (no-op).Buffer.compactas a native would be a new manifest row incompiler/src/backend/c/natives.trb(the!isBufferguard), i.e. a name the seed must know beforestd/uses it: the two-commit seed rule, which this issue cannot satisfy in one PR. It would also be a lie today:withCapacityis a hint the runtime drops, so no storage holds capacity beyond its length. A realcompactfollows when capacity becomes real.Bytes[a..b]copies.Buffer.sliceowns its storage whileList.sliceshares; COLLECTIONS.md 3.7 choseBuffer<UInt8>knowing that. Accepted, guarded by the performance criterion (hot sites: inflate's window copy, the binary readers indocs/PERFORMANCE.mdsection onbytes[from..to]) and by the allocation budgets, not by keeping the old type. Numbers on the existing code: 340 spellings ofList<UInt8>in 116 files plus 18 module natives typedArrayList<UInt8>; the sweep is mechanical and checker-driven (as in 6d: rename, read every "has no member/mismatch" until none is left).Buffer<UInt8>(sameListStorage, same C symbols) instead ofBuffer.from(arrayList)at every call, which would copy every chunk of every read. The generated header must stay unchanged, whichcompiler/tests/natives.test.trbverifies.Buffer<UInt8>takesisBuiltThroughFrom(Some(symbol) != ArrayList) tobuiltTargetOf, asRingListdoes today;Bufferalready hasFrom<Iterate<Item>>. No string lookup ofBytesexists incompiler/src, so the seed is not touched (the only name-keyed sites areList,ArrayListand the primitiveBuffer).benchmarks/README.md), andbenchmarks.ymlmeasures releases, not branches. So the criterion is a same-run pair (Buffer<UInt8>vsList<UInt8>program, within 10 %) plus the compression round trip at commit 1 against commit 2, both read in forge CI on the branch; 10 % because the fastest-of-9 noise on that runner is well under it..allocations. Budgets may rise only where a slice used to share and now copies, each one listed with numbers in the PR;.expectedstays byte-identical. Otherwise a rise would hide a regression.BytesandList<UInt8>stop being one type. Not an owner question: COLLECTIONS.md 3.7 already records the decision to makeBytesaBuffer<UInt8>, andstd/is 0.x.[loop:torbscript] refine: ready (one PR, two commits:
feat(collections): Buffer is a Listwith tests, a conformance program and the benchmark pair; thenrefactor(stream): Bytes is Buffer<UInt8>with the checker-driven sweep ofList<UInt8>and 18 natives, budgets and docs; not seed-touching; dependency #54 landed; the "not ready" note in the old body is obsolete and was removed; measured acceptance criteria in forge CI), next: implement. Claim released.[loop:torbscript] claim implement by session:loop-torbscript-20261018 until 2026-10-18T04:46Z
orion referenced this issue2026-10-17 23:02:12 +00:00
[loop:torbscript] implement: commit 2
refactor(stream): Bytes is Buffer<UInt8>is up and CI is green on all jobs (bootstrap, tier A, tier B, release-sync/registry, every-host C, playground); PR #383, next: review (waiting for merge gate argus)list-slice-override164 -> 165,standard-streams207 -> 212 (LinesAccumulator.addslices).buffer-field-derivedis new at 44. Nothing else moved.appendwrapper for an empty literal.ArrayList, the hash tables and the tries onBuffer(step 1 of #55) #413ArrayList, the hash tables and the tries onBuffer#55std/tensoron the CPU (step 1 of #82) #438std/tensoron the CPU #82std/tensoron the CPU #82std/tensoron the CPU (step 1 of #82) #438std/tensor,Tensor<Scalar>over aBuffer(step 2 of #82) #691