Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ under the licensing terms detailed in LICENSE:
* Fabián Heredia Montiel <[email protected]>
* Jonas Minnberg <[email protected]>
* Kam Chehresa <[email protected]>
* Pebrian <[email protected]>

Portions of this software are derived from third-party works licensed under
the following terms:
Expand Down
10 changes: 9 additions & 1 deletion std/assembly/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2394,7 +2394,7 @@ interface NewableFunction extends Function {
interface IArguments {}
interface RegExp {}

declare class Map<K,V> {
interface Map<K,V> {
readonly size: i32;
has(key: K): bool;
set(key: K, value: V): this;
Expand All @@ -2406,6 +2406,14 @@ declare class Map<K,V> {
toString(): string;
}

type MapInitialEntries<K,V> = {key: V, value:V}[]

interface MapConstructor {
new <K, V>(entries?: MapInitialEntries<K,V>): Map<K,V>;
}

declare const Map: MapConstructor;

declare class Set<K> {
readonly size: i32;
has(value: K): bool;
Expand Down
47 changes: 45 additions & 2 deletions std/assembly/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ function ENTRY_SIZE<K,V>(): usize {
return size;
}

class KeyValue<K,V> {
key: K;
value: V;
}

export type MapInitialEntries<K,V> = KeyValue<K,V>[];

export class Map<K,V> {

// buckets referencing their respective first entry, usize[bucketsMask + 1]
Expand All @@ -66,8 +73,44 @@ export class Map<K,V> {
private entriesOffset: i32 = 0;
private entriesCount: i32 = 0;

constructor() {
/* nop */
constructor(initialEntries: MapInitialEntries<K,V> = []) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be nullable, and the default argument should be null instead of []. Tip: if you wrap the entire constructor body with if (initialEntries) {, you can avoid doing initialEntries!.

(I wonder if this change could be beneficial for code that never uses this feature, by making it easier for Binaryen to optimize)

let entriesLength = initialEntries.length;

if(entriesLength > 0) {
if(entriesLength >= this.entriesCapacity) this.bucketsMask = entriesLength;
this.rehash((this.bucketsMask << 1) | 1);

for(let i = 0; i < entriesLength; i++){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if(entriesLength > 0) {
if(entriesLength >= this.entriesCapacity) this.bucketsMask = entriesLength;
this.rehash((this.bucketsMask << 1) | 1);
for(let i = 0; i < entriesLength; i++){
if (entriesLength > 0) {
if (entriesLength >= this.entriesCapacity) this.bucketsMask = entriesLength;
this.rehash((this.bucketsMask << 1) | 1);
for (let i = 0; i < entriesLength; i++) {

let key = initialEntries[i].key;
let value = initialEntries[i].value;
let hashCode = HASH<K>(key);
let entry = this.find(key, hashCode); // unmanaged!
if (entry) {
entry.value = value;
if (isManaged<V>()) {
__link(changetype<usize>(this), changetype<usize>(value), true);
}
} else {
// append new entry
let entries = this.entries;
let entry = changetype<MapEntry<K,V>>(changetype<usize>(entries) + <usize>(this.entriesOffset++) * ENTRY_SIZE<K,V>());
// link with the map
entry.key = key;
if (isManaged<K>()) {
__link(changetype<usize>(this), changetype<usize>(key), true);
}
entry.value = value;
if (isManaged<V>()) {
__link(changetype<usize>(this), changetype<usize>(value), true);
}
++this.entriesCount;
// link with previous entry in bucket
let bucketPtrBase = changetype<usize>(this.buckets) + <usize>(hashCode & this.bucketsMask) * BUCKET_SIZE;
entry.taggedNext = load<usize>(bucketPtrBase);
store<usize>(bucketPtrBase, changetype<usize>(entry));
}
}
}
}

get size(): i32 {
Expand Down
21 changes: 21 additions & 0 deletions tests/compiler/std/map.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

function testNumeric<K extends number,V extends number>(): void {
var map = new Map<K,V>();

Expand Down Expand Up @@ -58,6 +59,26 @@ function testNumeric<K extends number,V extends number>(): void {
// clear
map.clear();
assert(map.size == 0);

const initialEntries: MapInitialEntries<K,V> = [];

for(let k: K = 0; k < 100; ++k){
initialEntries.push({
key: k,
value: 10 + <V>k
});
}

map = new Map<K,V>(initialEntries);

assert(map.size == 100);
for(let k: K = 0; k < 100; ++k){
assert(map.has(k));
assert(map.get(k) == 10 + <V>k);
map.delete(k);
assert(!map.has(k));
}
assert(map.size == 0);
}

testNumeric<i8,i32>();
Expand Down
Loading