Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Readme: undefined behavior for nullish range options is legit #305

Merged
merged 1 commit into from
Sep 23, 2018
Merged
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
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,11 @@ Legacy options:
- `start`: instead use `gte`
- `end`: instead use `lte`.

Note that `null`, `undefined`, zero-length strings, zero-length buffers and zero-length arrays are invalid as keys, yet valid as range options. These types are significant in encodings like [`bytewise`](https://github.com/deanlandolt/bytewise) and [`charwise`](https://github.com/dominictarr/charwise). An `abstract-leveldown` implementation is expected to either [*encode*][encoding-down] these range option types, [*serialize*](#private-serialize-key) them, *delegate* to an underlying store, or finally, *ignore* them.
**Note** Zero-length strings, buffers and arrays as well as `null` and `undefined` are invalid as keys, yet valid as range options. These types are significant in encodings like [`bytewise`](https://github.com/deanlandolt/bytewise) and [`charwise`](https://github.com/dominictarr/charwise). Consumers of an implementation should assume that `{ gt: undefined }` is *not* the same as `{}`. An implementation can choose to:

- [*Serialize*](#private-serialize-key) or [*encode*][encoding-down] these types to make them meaningful
- Have no defined behavior (moving the concern to a higher level)
- Delegate to an underlying store (moving the concern to a lower level).

In addition to range options, `iterator()` takes the following options:

Expand Down Expand Up @@ -278,15 +282,15 @@ Close the store. If closing failed, call the `callback` function with an `Error`
<a name="private-serialize-key"></a>
### `db._serializeKey(key)`

Convert a `key` to a type supported by the underlying storage. All methods below that take a `key` argument or option - as well as `_seek(target)` - will receive serialized keys. For example, if `_serializeKey` is implemented as:
Convert a `key` to a type supported by the underlying storage. All methods below that take a `key` argument or option - including `db._iterator()` with its range options and `iterator._seek()` with its `target` argument - will receive serialized keys. For example, if `_serializeKey` is implemented as:

```js
FakeLevelDOWN.prototype._serializeKey = function (key) {
return Buffer.isBuffer(key) ? key : String(key)
}
```

Then `db.get(2, callback)` will translate into `db._get('2', options, callback)`.
Then `db.get(2, callback)` translates into `db._get('2', options, callback)`. Similarly, `db.iterator({ gt: 2 })` translates into `db._iterator({ gt: '2', ... })` and `iterator.seek(2)` translates into `iterator._seek('2')`.

If the underlying storage supports any JavaScript type or if your implementation wraps another implementation, it is recommended to make `_serializeKey` an identity function. Serialization is irreversible, unlike *encoding* as performed by implementations like [`encoding-down`][encoding-down]. This also applies to `_serializeValue`.

Expand All @@ -301,7 +305,7 @@ FakeLevelDOWN.prototype._serializeValue = function (value) {
}
```

Then `db.put(key, 2, callback)` will translate into `db._put(key, '2', options, callback)`.
Then `db.put(key, 2, callback)` translates into `db._put(key, '2', options, callback)`.

### `db._get(key, options, callback)`

Expand Down