Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion quest/m3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@ generic std gap is planned here even when moq never calls it.
- [Fresh writes beside a zipped iterator that points at a part](/quest/m3/ts-fresh-shapes.md) - zipping slots with a `Vec<[T; N]>`, `vec![row; n]`, or `copied` of what the slot holds translates
- [Python's wake on the loop's own thread](/quest/m3/ffi-python-wake.md) - a wake on the event loop's thread resumes the future with no socket pair
- [Python's call cost](/quest/m3/ffi-python-calls.md) - a call on an object costs what BoltFFI's does, or the gap is explained
- [A std map with a crate's own BuildHasher](/quest/m3/ts-map-own-hasher.md) - std's and hashbrown's maps built with a crate's own hasher translate for keys that are JS objects
- [A map built with BuildHasherDefault](/quest/m3/ts-build-hasher-default.md) - `FxHashMap`-style maps, of `BuildHasherDefault` over a crate's own `Hasher`, translate
- [Equality of hash maps and sets](/quest/m3/ts-hash-map-eq.md) - `==` of two hash maps or sets translates as std's
- [A name or an operator argument at its own Rust span](/quest/m3/ts-argument-spans.md) - a `tsc` error on a plain-name or operator argument names that argument's Rust, not the method it is passed to
21 changes: 21 additions & 0 deletions quest/m3/ts-build-hasher-default.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# [S] A map built with BuildHasherDefault

## Goal

[rust2-ts](/ts/README.md) translates `BuildHasherDefault<H>`, std's `BuildHasher` of a
`Hasher` that is `Default`, as a map's hasher, as `rustc-hash`'s `FxHashMap` and
`nohash-hasher`'s `IntMap` build theirs: making one, `Default`, `Clone`, `build_hasher`,
and `hash_one` through a crate's own `H`, proven by differential tests against native Rust.

## Plan

Found while landing maps that hold a crate's own `BuildHasher`, which refuse
`BuildHasherDefault` as "unsupported type `std::hash::BuildHasherDefault` from another
crate".

- It holds nothing but a `PhantomData`, so it may be what such a type is, as `PhantomData`
is, and its `build_hasher` is `H::default()`.
- A map already holds any `BuildHasher` but `RandomState` and hashes keys around it.
- No consumer yet; planned here because rust2 aims to support all of std.

Public API: the translator's output.
19 changes: 19 additions & 0 deletions quest/m3/ts-hash-map-eq.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# [S] Equality of hash maps and sets

## Goal

[rust2-ts](/ts/README.md) translates `==` and `!=` of two `HashMap`s or two `HashSet`s,
std's and hashbrown's, whatever their `BuildHasher`, as std's: equal lengths, then each
entry of one found in the other with an equal value, proven by differential tests against
native Rust.

## Plan

Found while landing maps that hold a crate's own `BuildHasher`: `PartialEq::eq` of two
maps is refused as an unsupported call, for a `RandomState` map too.

- std's walks one map and looks each key up in the other, calling the value's
`PartialEq`; which map it walks decides which value's `eq` runs as `self`.
- No consumer yet; planned here because rust2 aims to support all of std.

Public API: the translator's output.
25 changes: 0 additions & 25 deletions quest/m3/ts-map-own-hasher.md

This file was deleted.

6 changes: 5 additions & 1 deletion ts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,11 @@ such as a number, from `get_mut` or the entry API, is an `rt.Place` whose `v` re
writes the map's slot. A hash map iterates in an order
of the runtime's, mostly insertion order, which std leaves unspecified, and hashes a key
with a hasher of its own, as std's keys its at random. So is how often the runtime
calls a key's `Hash`, `Eq`, or `Ord`.
calls a key's `Hash`, `Eq`, or `Ord`. A map or set built with any `BuildHasher` but
`RandomState` holds it, which `hasher` lends, `Default` and `collect` make, and `clone`
clones before what the map holds; it drops before the map's entries, and at once from
`into_iter`, `into_keys`, and `into_values`, as hashbrown's does. No key goes through it:
the runtime's own hasher finds them, so it builds no hasher for the map.

A `DefaultHasher` is std's SipHash-1-3, and `Hash` writes into it what std's impls write,
so `DefaultHasher::new`'s hash of a value is native's. `RandomState::new` keys it as std
Expand Down
24 changes: 24 additions & 0 deletions ts/fixtures/lang/hashes/expected/harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,28 @@ export function op(name: string): rt.u16 {
}
}

/** Write `s` and the byte no UTF-8 holds, which ends it. */
export function put(out: rt.ByteVec, s: string): void {
rt.byteVecExtendFromSlice(out, rt.stringAsBytes(s));
rt.byteVecPush(out, 255);
}

/** A char of the alphabet for each byte, by its low bits. */
export function text(data: rt.ByteSlice): string {
let s: string = "";
const iterS: Uint8Array = rt.byteSliceView(data);
let iterI: number = 0;
while (true) {
const _5: rt.u8 | rt.Done = iterI < iterS.length ? iterS[iterI++] : rt.DONE;
if (_5 === rt.DONE) {
return s;
}
const _13: rt.usize = rt.u8Rem(_5, 16) as rt.usize;
if (!(_13 < 16)) {
rt.panic("index out of bounds: the len is " + 16 + " but the index is " + _13);
}
s = s + String.fromCodePoint([47, 92, 97, 48, 57, 127, 128, 233, 2047, 2048, 55295, 57344, 65535, 65536, 128512, 1114111][_13]);
}
}

//# sourceMappingURL=harness.ts.map
26 changes: 26 additions & 0 deletions ts/fixtures/lang/hashes/expected/hashes.Fnv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Generated by rust2-ts. Do not edit.

import * as rt from "./rt.ts";

/**
* FNV-1a, a `Hasher` of the crate's own that logs nothing, so a map may call it as often
* as it likes.
*/
export interface Fnv {
_0: rt.u64;
}

export function finish$hash_Hasher(self: Fnv): rt.u64 {
return self._0;
}

/** The methods of `hashes::Fnv`'s impl, which the runtime's hashing calls. */
export const hasher$hash_Hasher: rt.HasherImpl<Fnv> = { write: write$hash_Hasher as (a0: Fnv, a1: rt.ByteSlice) => undefined, finish: finish$hash_Hasher };

export function write$hash_Hasher(self: Fnv, bytes: rt.ByteSlice): void {
for (const b of rt.byteSliceView(bytes)) {
self._0 = rt.u64WrappingMul(rt.u64Xor(self._0, b as rt.u64), 1099511628211);
}
}

//# sourceMappingURL=hashes.Fnv.ts.map
41 changes: 41 additions & 0 deletions ts/fixtures/lang/hashes/expected/hashes.Key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Generated by rust2-ts. Do not edit.

import * as rt from "./rt.ts";
import * as Fnv from "./hashes.Fnv.ts";

export function eq$cmp_PartialEq(self: Key, other: Key): boolean {
if (!(self._0 === other._0)) {
return false;
}
return self._1 === other._1;
}

export function hash$Hash_hash_DefaultHasher(self: Key, state: rt.Hasher): void {
rt.hash16(self._0, state);
rt.hashStr(self._1, state);
}

export function hash$Hash_hashes_Fnv(self: Key, state: Fnv.Fnv): void {
rt.hash16(self._0, new rt.OwnHasher(state, Fnv.hasher$hash_Hasher));
rt.hashStr(self._1, new rt.OwnHasher(state, Fnv.hasher$hash_Hasher));
}

export function partialCmp$cmp_PartialOrd(self: Key, other: Key): rt.Ordering | undefined {
return cmp$cmp_Ord(self, other);
}

export function cmp$cmp_Ord(self: Key, other: Key): rt.Ordering {
const cmp: rt.Ordering = rt.cmp(self._0, other._0);
if (cmp === 0) {
return rt.cmpStr(self._1, other._1);
}
return cmp;
}

/** A key that is a JS object. */
export interface Key {
_0: rt.u16;
_1: string;
}

//# sourceMappingURL=hashes.Key.ts.map
28 changes: 28 additions & 0 deletions ts/fixtures/lang/hashes/expected/hashes.Noisy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Generated by rust2-ts. Do not edit.

import * as rt from "./rt.ts";
import { event } from "./hashes.ts";

/** A value that logs its clone and its drop. */
export class Noisy {
_0: rt.u8;

constructor(_0: rt.u8) {
this._0 = _0;
}

[Symbol.dispose](): void {
drop$ops_Drop(this);
}
}

export function clone$Clone(self: Noisy): Noisy {
event(67, self._0);
return new Noisy(self._0);
}

export function drop$ops_Drop(self: Noisy): void {
event(68, self._0);
}

//# sourceMappingURL=hashes.Noisy.ts.map
41 changes: 41 additions & 0 deletions ts/fixtures/lang/hashes/expected/hashes.Numbered.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Generated by rust2-ts. Do not edit.

import * as rt from "./rt.ts";
import { event } from "./hashes.ts";
import type * as Fnv from "./hashes.Fnv.ts";

/**
* A `BuildHasher` of the crate's own, numbered, which logs each one `Default` makes, each
* clone, and each drop, and seeds its [`Fnv`] by its number.
*/
export class Numbered {
_0: rt.u8;

constructor(_0: rt.u8) {
this._0 = _0;
}

[Symbol.dispose](): void {
drop$ops_Drop(this);
}
}

export function buildHasher$hash_BuildHasher(self: Numbered): Fnv.Fnv {
return { _0: rt.u64Xor({ hi: 0xcbf29ce4, lo: 0x84222325 } as rt.u64, self._0 as rt.u64) };
}

export function default$Default(): Numbered {
event(110, 0);
return new Numbered(0);
}

export function clone$Clone(self: Numbered): Numbered {
event(99, self._0);
return new Numbered(rt.u8WrappingAdd(self._0, 1));
}

export function drop$ops_Drop(self: Numbered): void {
event(120, self._0);
}

//# sourceMappingURL=hashes.Numbered.ts.map
117 changes: 116 additions & 1 deletion ts/fixtures/lang/hashes/expected/hashes.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
// Generated by rust2-ts. Do not edit.

import * as rt from "./rt.ts";
import { drop$hashes_Switch, drop$tuple2_hashes_Seeded_hashes_Seeded } from "./lib.ts";
import { drop$Option_tuple2_u8_hashes_Noisy, drop$hashes_Switch, drop$tuple2_hashes_Seeded_hashes_Seeded } from "./lib.ts";
import { thenSome$0n2gkz4 } from "./core/bool.ts";
import { unwrapOr$0k15iai as option_unwrapOr$0k15iai, unwrapOr$0r2bmpr as option_unwrapOr$0r2bmpr } from "./core/option.ts";
import { unwrapOr$01jdcm7 as result_unwrapOr$01jdcm7 } from "./core/result.ts";
import { put, text } from "./harness.ts";
import * as Bare from "./hashes.Bare.ts";
import * as Build from "./hashes.Build.ts";
import * as Counted from "./hashes.Counted.ts";
import * as Dir from "./hashes.Dir.ts";
import * as Fnv from "./hashes.Fnv.ts";
import * as Full from "./hashes.Full.ts";
import * as Holder from "./hashes.Holder.ts";
import * as Key from "./hashes.Key.ts";
import * as Logged from "./hashes.Logged.ts";
import * as Nibble from "./hashes.Nibble.ts";
import * as Noisy from "./hashes.Noisy.ts";
import * as Numbered from "./hashes.Numbered.ts";
import * as Packet from "./hashes.Packet.ts";
import * as Partial from "./hashes.Partial.ts";
import * as Priorities$random from "./hashes.Priorities$random.ts";
Expand Down Expand Up @@ -999,4 +1004,114 @@ export function copied(data: rt.ByteSlice, out: rt.ByteVec): void {
rt.byteVecExtendFromSlice(out, rt.byteVecSlice(log.value));
}

export const EVENTS: rt.LocalKey<rt.RefCell<rt.ByteVec>> = /* @__PURE__ */ new rt.LocalKey(EVENTS$init, false);

export function EVENTS$init(): rt.RefCell<rt.ByteVec> {
return new rt.RefCell(new rt.ByteVec());
}

/** Log `what` happened to the value numbered `n`. */
export function event(what: rt.u8, n: rt.u8): void {
((events: rt.RefCell<rt.ByteVec>): undefined => {
const _52: rt.RefCell<rt.ByteVec> = rt.refCellBorrowMut(events);
rt.iterExtendBytes(_52.value, rt.iterSlice(Uint8Array.of(what, n)), undefined, true);
rt.refMutRelease(_52);
})(EVENTS.get());
}

/**
* Hash maps and sets with a `BuildHasher` of the crate's own, keyed by JS objects and by
* numbers, which find their keys whatever it hashes them to. Each holds its own, which
* `hasher` lends, made by `Default` where std's makes one, cloned before what the map
* holds, and dropped before it, and at once by a consuming iterator. Only maps of one
* entry say what they drop or clone, and only sorted what they hold, as std's order is
* its own.
*/
export function maps(data: rt.ByteSlice, out: rt.ByteVec): void {
((events: rt.RefCell<rt.ByteVec>): undefined => {
const _5: rt.RefCell<rt.ByteVec> = rt.refCellBorrowMut(events);
rt.byteVecClear(_5.value);
rt.refMutRelease(_5);
})(EVENTS.get());
const first: rt.u8 = option_unwrapOr$0r2bmpr(rt.byteSliceFirst(data), 7);
const keyed: rt.HashMap<Key.Key, rt.u8> = rt.hashWithCapacity(data.length, 58720256, rt.hashing(Key.hash$Hash_hash_DefaultHasher as (a0: Key.Key, a1: rt.Hasher) => undefined, Key.eq$cmp_PartialEq), new Numbered.Numbered(first)) as rt.HashMap<Key.Key, rt.u8>;
const seen: rt.HashSet<rt.u8> = rt.hashNew(undefined, new Numbered.Numbered(1)) as rt.HashSet<rt.u8>;
const iter: rt.Iter<[rt.usize, rt.u8]> = rt.iterEnumerate(rt.iterSlice(rt.byteSliceView(data)));
while (true) {
const _17: [rt.usize, rt.u8] | rt.Done = iter.next();
if (_17 === rt.DONE) {
break;
}
const i: rt.usize = _17[0];
const b: rt.u8 = _17[1];
const _26: rt.u16 = rt.u8Rem(b, 8) as rt.u16;
const bounds = rt.sliceIndexBounds(new rt.RangeInclusive(i, i, rt.usizeStep), data.length);
const _25: Key.Key = { _0: _26, _1: text(rt.byteSliceRange(data, bounds[0], bounds[1])) };
const _22: rt.Place<rt.u8> = rt.entryOrInsertWithMut(rt.entry(keyed, _25), (): rt.u8 => {
return 0;
});
_22.v = rt.u8Add(_22.v, 1);
rt.byteVecPush(out, rt.hashAdd(seen, rt.u8Rem(b, 16)) ? 1 : 0);
const _44: Key.Key = { _0: (rt.u8Rem(b, 8) ^ 1) as rt.u16, _1: text(rt.byteSliceFrom(Uint8Array.of(b ^ 1))) };
rt.byteVecPush(out, rt.hashContains(keyed, _44) ? 1 : 0);
}
const held: [Key.Key, rt.u8][] = rt.iterToArray(rt.hashIter(keyed), "next", 8);
rt.sliceSort(held, rt.cmpLt(rt.cmpTuple<[Key.Key, rt.u8], rt.Ordering | undefined>([Key.partialCmp$cmp_PartialOrd, rt.cmp])), 8, true);
for (const _63 of held) {
rt.byteVecPush(out, _63[0]._0 & 0xff);
put(out, _63[0]._1);
rt.byteVecPush(out, _63[1]);
}
const probe: Key.Key = { _0: first as rt.u16, _1: text(rt.byteSliceFrom(Uint8Array.of(first))) };
rt.byteVecPush(out, (keyed.s as Numbered.Numbered)._0);
rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u64ToBeBytes(rt.hashOne(new rt.OwnHasher(Numbered.buildHasher$hash_BuildHasher(keyed.s as Numbered.Numbered), Fnv.hasher$hash_Hasher), probe, rt.hashOwn(Key.hash$Hash_hashes_Fnv as (a0: Key.Key, a1: Fnv.Fnv) => undefined))).reverse()));
rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(rt.u64ToBeBytes(rt.hashOne(new rt.OwnHasher(Numbered.buildHasher$hash_BuildHasher(seen.s as Numbered.Numbered), Fnv.hasher$hash_Hasher), first, rt.hash8)).reverse()));
const noisy: rt.HashMap<rt.u8, Noisy.Noisy> = rt.hashNew(undefined, new Numbered.Numbered(5)) as rt.HashMap<rt.u8, Noisy.Noisy>;
rt.drop(rt.hashInsert(noisy, first, new Noisy.Noisy(first)));
const copy: rt.HashMap<rt.u8, Noisy.Noisy> = rt.hashClone(noisy, undefined, Noisy.clone$Clone, Numbered.clone$Clone);
rt.byteVecPush(out, (copy.s as Numbered.Numbered)._0);
rt.hashDrop(noisy, undefined, rt.drop, rt.drop);
const entries: rt.Iter<[rt.u8, Noisy.Noisy]> = rt.hashIter(copy, undefined, rt.drop, rt.drop);
event(105, 0);
drop$Option_tuple2_u8_hashes_Noisy(rt.iterToOption(entries.next()));
rt.drop(entries);
let made: rt.HashMap<rt.u8, Noisy.Noisy> = rt.hashNew(undefined, Numbered.default$Default()) as rt.HashMap<rt.u8, Noisy.Noisy>;
rt.drop(rt.hashInsert(made, 2, new Noisy.Noisy(2)));
const default_ = rt.hashNew(undefined, Numbered.default$Default()) as rt.HashMap<rt.u8, Noisy.Noisy>;
const taken2 = made;
made = default_;
const taken: rt.HashMap<rt.u8, Noisy.Noisy> = taken2;
rt.byteVecPush(out, rt.hashLen(taken) & 0xff);
rt.byteVecPush(out, rt.hashLen(made) & 0xff);
const _136: rt.Iter<rt.u8> = rt.iterMap(rt.iterSlice(rt.byteSliceView(data)), (b2: rt.u8): rt.u8 => {
return rt.u8Rem(b2, 4);
});
const small: rt.HashSet<rt.u8> = rt.hashExtendKeys(rt.hashNew(undefined, Numbered.default$Default()) as rt.HashSet<rt.u8>, _136, undefined, undefined, 469762048);
const union: rt.HashSet<rt.u8> = rt.hashExtendKeys(rt.hashNew(undefined, Numbered.default$Default()) as rt.HashSet<rt.u8>, rt.hashUnion(small, seen), undefined, undefined, 469762048);
rt.byteVecPush(out, rt.hashLen(union) & 0xff);
rt.byteVecPush(out, rt.hashIsSubset(small, seen) ? 1 : 0);
rt.hashExtendKeys(seen, rt.hashKeys(small, undefined, undefined, rt.drop), undefined, undefined, 469762048);
rt.byteVecPush(out, rt.hashLen(seen) & 0xff);
const keys: Key.Key[] = rt.iterToArray(rt.hashKeys(keyed, undefined, undefined, rt.drop), "next", 16);
rt.sliceSort(keys, rt.cmpLt(Key.partialCmp$cmp_PartialOrd), 16, true);
rt.byteVecPush(out, keys.length & 0xff);
const iter3: rt.Iter<Noisy.Noisy> = rt.hashValues(taken, rt.drop, undefined, rt.drop);
while (true) {
const _169: Noisy.Noisy | rt.Done = iter3.next();
if (_169 === rt.DONE) {
break;
}
const value: Noisy.Noisy = _169;
event(118, value._0);
value[Symbol.dispose]();
}
rt.drop(iter3);
rt.hashDrop(union, undefined, undefined, rt.drop);
rt.byteVecExtendFromSlice(out, rt.byteVecSlice(((events2: rt.RefCell<rt.ByteVec>): rt.ByteVec => {
return rt.refCellReplace(events2, new rt.ByteVec());
})(EVENTS.get())));
rt.hashDrop(made, undefined, rt.drop, rt.drop);
rt.hashDrop(seen, undefined, undefined, rt.drop);
}

//# sourceMappingURL=hashes.ts.map
Loading