Skip to content

Commit f5cfdb8

Browse files
committed
Add EnumType
1 parent 97c559f commit f5cfdb8

File tree

10 files changed

+545
-0
lines changed

10 files changed

+545
-0
lines changed

src/index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {make as makeArrayType} from "./type-class/array-type";
99
import {make as makeHashMapType} from "./type-class/hash-map-type";
1010
import {make as makeHashSetType} from "./type-class/hash-set-type";
1111
import {make as makeUnionType} from "./type-class/union-type";
12+
import {make as makeEnumType} from "./type-class/enum-type";
1213

1314
import {make as makeStringPool} from "./string-pool";
1415

@@ -73,6 +74,7 @@ export class Realm {
7374
this.HashMapType = makeHashMapType(this);
7475
this.HashSetType = makeHashSetType(this);
7576
this.UnionType = makeUnionType(this);
77+
this.EnumType = makeEnumType(this);
7678
this.isInitialized = false;
7779
}
7880

@@ -150,6 +152,11 @@ export class Realm {
150152
this[$RootHashMap].set(key, value);
151153
return this;
152154
}
155+
156+
delete (key: any): boolean {
157+
// @flowIssue 252
158+
return this[$RootHashMap].delete(key);
159+
}
153160
}
154161

155162

src/type-class/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,5 @@ Builtin Type Classes:
1212
* [ReferenceType](./reference-type)
1313
* [StringType](./string-type)
1414
* [StructType](./struct-type)
15+
* [EnumType](./enum-type)
16+
* [UnionType](./union-type)

src/type-class/enum-type/README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# EnumType
2+
3+
A type class for representing [enumerated values](https://en.wikipedia.org/wiki/Enumerated_type).
4+
5+
Enums efficiently represent a single value out of a predefined set. Unlike most types, they can store *any* kind of value, including symbols and functions. This is because they don't actually store values themselves, instead they store references.
6+
7+
Usage:
8+
9+
```js
10+
const {EnumType, StructType, T} = realm;
11+
12+
const EmailStatus = new EnumType("read", "unread", "pending", "sent");
13+
14+
const inbox = {
15+
name: 'inbox'
16+
};
17+
const drafts = {
18+
name: 'drafts'
19+
};
20+
const outbox = {
21+
name: 'outbox'
22+
};
23+
const EmailContainer = new EnumType(inbox, drafts, outbox);
24+
25+
const Email = new StructType({
26+
subject: T.String,
27+
status: EmailStatus,
28+
container: EmailContainer,
29+
body: T.String
30+
});
31+
32+
const message = new Email({
33+
subject: 'Hello World',
34+
status: 'pending',
35+
container: drafts,
36+
message: 'Hi.'
37+
});
38+
39+
message.status = 'nope'; // TypeError - not in the list of allowed values.
40+
message.status = 'sent';
41+
message.container = outbox;
42+
```

src/type-class/enum-type/index.js

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
/* @flow */
2+
3+
import Backing from "backing";
4+
import {inspect} from "util";
5+
import {TypedObject} from "../";
6+
import {alignTo} from "../../util";
7+
8+
import type {Realm} from "../../";
9+
10+
import {
11+
$ValueType,
12+
$Backing,
13+
$Address,
14+
$CanBeEmbedded,
15+
$CanBeReferenced,
16+
$CanContainReferences
17+
} from "../../symbols";
18+
19+
export const MIN_TYPE_ID = Math.pow(2, 20) * 9;
20+
21+
/**
22+
* Makes a EnumType type class for the given realm.
23+
*/
24+
export function make (realm: Realm): TypeClass<EnumType<any>> {
25+
const {TypeClass} = realm;
26+
let typeCounter = 0;
27+
28+
function createTinyEnumType (Enum: Function, possibleValues: Type[]): Object {
29+
const name = possibleValues.map(value => inspect(value)).join(' | ');
30+
const id = MIN_TYPE_ID + typeCounter;
31+
const byteAlignment = 1;
32+
const byteLength = 1;
33+
34+
function enumAccepts (input: any): boolean {
35+
const length = possibleValues.length;
36+
for (let i = 0; i < length; i++) {
37+
if (input === possibleValues[i]) {
38+
return true;
39+
}
40+
}
41+
return false;
42+
}
43+
44+
function indexFor (value: any): number {
45+
const length = possibleValues.length;
46+
for (let i = 0; i < length; i++) {
47+
if (value === possibleValues[i]) {
48+
return i;
49+
}
50+
}
51+
return -1;
52+
}
53+
54+
55+
function initializeEnum (backing: Backing, address: float64, initialValue: any): void {
56+
let index = indexFor(initialValue);
57+
if (index === -1) {
58+
if (initialValue === undefined) {
59+
index = 0;
60+
}
61+
else {
62+
throw new TypeError(`Enum does not contain the given value: ${inspect(initialValue)}`);
63+
}
64+
}
65+
backing.setUint8(address, index);
66+
}
67+
68+
function storeEnum (backing: Backing, address: float64, value: any): void {
69+
const index = indexFor(value);
70+
if (index === -1) {
71+
throw new TypeError(`Enum does not contain the given value: ${inspect(value)}`);
72+
}
73+
backing.setUint8(address, index);
74+
}
75+
76+
function loadEnum (backing: Backing, address: float64): any {
77+
return possibleValues[backing.getUint8(address)];
78+
}
79+
80+
function clearEnum (backing: Backing, address: float64): void {
81+
backing.setUint8(address, 0);
82+
}
83+
84+
function enumDestructor (backing: Backing, address: float64): void {
85+
// no-op
86+
}
87+
88+
return {
89+
id,
90+
name,
91+
byteLength,
92+
byteAlignment,
93+
accepts: enumAccepts,
94+
initialize: initializeEnum,
95+
store: storeEnum,
96+
load: loadEnum,
97+
clear: clearEnum,
98+
destructor: enumDestructor,
99+
emptyValue (): any {
100+
return possibleValues[0];
101+
},
102+
randomValue (): any {
103+
return possibleValues[Math.floor(Math.random() * possibleValues.length)];
104+
}
105+
};
106+
107+
}
108+
109+
function createLargeEnumType (Enum: Function, possibleValues: Type[]): Object {
110+
const name = possibleValues.map(value => inspect(value)).join(' | ');
111+
const id = MIN_TYPE_ID + typeCounter;
112+
const byteAlignment = 2;
113+
const byteLength = 2;
114+
const valueMap = new Map(possibleValues.map((value, index) => [value, index]));
115+
116+
function enumAccepts (input: any): boolean {
117+
return valueMap.has(input);
118+
}
119+
120+
function indexFor (value: any): number {
121+
const index = valueMap.get(value);
122+
if (typeof index === 'number') {
123+
return index;
124+
}
125+
else {
126+
return -1;
127+
}
128+
}
129+
130+
131+
function initializeEnum (backing: Backing, address: float64, initialValue: any): void {
132+
let index = indexFor(initialValue);
133+
if (index === -1) {
134+
if (initialValue === undefined) {
135+
index = 0;
136+
}
137+
else {
138+
throw new TypeError(`Enum does not contain the given value: ${inspect(initialValue)}`);
139+
}
140+
}
141+
backing.setUint16(address, index);
142+
}
143+
144+
function storeEnum (backing: Backing, address: float64, value: any): void {
145+
const index = indexFor(value);
146+
if (index === -1) {
147+
throw new TypeError(`Enum does not contain the given value: ${inspect(value)}`);
148+
}
149+
backing.setUint16(address, index);
150+
}
151+
152+
function loadEnum (backing: Backing, address: float64): any {
153+
return possibleValues[backing.getUint16(address)];
154+
}
155+
156+
function clearEnum (backing: Backing, address: float64): void {
157+
backing.setUint16(address, 0);
158+
}
159+
160+
function enumDestructor (backing: Backing, address: float64): void {
161+
// no-op
162+
}
163+
164+
return {
165+
id,
166+
name,
167+
byteLength,
168+
byteAlignment,
169+
accepts: enumAccepts,
170+
initialize: initializeEnum,
171+
store: storeEnum,
172+
load: loadEnum,
173+
clear: clearEnum,
174+
destructor: enumDestructor,
175+
emptyValue (): any {
176+
return possibleValues[0];
177+
},
178+
randomValue (): any {
179+
return possibleValues[Math.floor(Math.random() * possibleValues.length)];
180+
}
181+
};
182+
183+
}
184+
185+
return new TypeClass('EnumType', (...possibleValues: Type[]) => {
186+
return (Enum: Function): Object => {
187+
typeCounter++;
188+
189+
let EnumArray;
190+
// @flowIssue 285
191+
Object.defineProperties(Enum, {
192+
Array: {
193+
get () {
194+
if (EnumArray === undefined) {
195+
EnumArray = new realm.ArrayType(Enum);
196+
}
197+
return EnumArray;
198+
}
199+
}
200+
});
201+
202+
Enum[$CanBeEmbedded] = true;
203+
Enum[$CanBeReferenced] = false;
204+
Enum[$CanContainReferences] = false;
205+
206+
if (possibleValues.length >= Math.pow(2, 16)) {
207+
throw new RangeError(`Enum can only store up to ${Math.pow(2, 16)} values.`);
208+
}
209+
else if (possibleValues.length >= 256) {
210+
return createLargeEnumType(Enum, possibleValues);
211+
}
212+
else {
213+
return createTinyEnumType(Enum, possibleValues);
214+
}
215+
};
216+
});
217+
};

0 commit comments

Comments
 (0)