From 7f9e60aa1ae9f7da7227c333c1b25809d7ea3c4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Sat, 25 Feb 2023 21:29:59 +0100 Subject: [PATCH] lib,src: fix a few typos in comments PR-URL: https://github.com/nodejs/node/pull/46835 Reviewed-By: Rich Trott Reviewed-By: Moshe Atlow Reviewed-By: Marco Ippolito Reviewed-By: Richard Lau Reviewed-By: Mohammed Keyvanzadeh --- lib/_http_client.js | 4 +- lib/_tls_wrap.js | 2 +- src/base_object.h | 2 +- src/dataqueue/queue.h | 294 +++++++++++++++++++++++++++++++++++++++++ src/node_contextify.cc | 2 +- 5 files changed, 299 insertions(+), 5 deletions(-) create mode 100644 src/dataqueue/queue.h diff --git a/lib/_http_client.js b/lib/_http_client.js index cd161fee62f5f4..55f058944ce5f7 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -786,8 +786,8 @@ function responseOnTimeout() { res.emit('timeout'); } -// This function is necessary in the case where we receive the entire reponse -// from server before we finish sending out the request +// This function is necessary in the case where we receive the entire response +// from the server before we finish sending out the request. function requestOnFinish() { const req = this; diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 095297f96a7b76..819dac9c431a8b 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -426,7 +426,7 @@ function onerror(err) { // so self._tlsError will return null instead of actual error // Set closing the socket after emitting an event since the socket needs to - // be accessible when the `tlsClientError` event is emmited. + // be accessible when the `tlsClientError` event is emitted. owner._closeAfterHandlingError = true; owner.destroy(err); } else if (owner._tlsOptions?.isServer && diff --git a/src/base_object.h b/src/base_object.h index 779573362268a6..7bfcb95cfcab65 100644 --- a/src/base_object.h +++ b/src/base_object.h @@ -249,7 +249,7 @@ inline T* Unwrap(v8::Local obj) { // reset to nullptr once the BaseObject is destroyed. // The API matches std::shared_ptr closely. However, this class is not thread // safe, that is, we can't have different BaseObjectPtrImpl instances in -// different threads refering to the same BaseObject instance. +// different threads referring to the same BaseObject instance. template class BaseObjectPtrImpl final { public: diff --git a/src/dataqueue/queue.h b/src/dataqueue/queue.h new file mode 100644 index 00000000000000..9bf34ffb6572aa --- /dev/null +++ b/src/dataqueue/queue.h @@ -0,0 +1,294 @@ +#pragma once + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace node { + +// Represents a sequenced collection of data sources that can be +// consumed as a single logical stream of data. Sources can be +// memory-resident or streaming. +// +// There are two essential kinds of DataQueue: +// +// * Idempotent - Multiple reads always produce the same result. +// This is even the case if individual sources +// are not memory-resident. Reads never change +// the state of the DataQueue. Every entry in +// an Idempotent DataQueue must also be idempotent. +// +// * Non-idempotent - Reads are destructive of the internal state. +// A non-idempotent DataQueue can be read at +// most once and only by a single reader. +// Entries in a non-idempotent DataQueue can +// be a mix of idempotent and non-idempotent +// entries. +// +// The DataQueue is essentially a collection of DataQueue::Entry +// instances. A DataQueue::Entry is a single logical source of +// data. The data may be memory-resident or streaming. The entry +// can be idempotent or non-idempotent. An entry cannot be read +// by itself, it must be part of a DataQueue to be consumed. +// +// Example of creating an idempotent DataQueue: +// +// std::shared_ptr store1 = getBackingStoreSomehow(); +// std::shared_ptr store2 = getBackingStoreSomehow(); +// +// std::vector> list; +// list.push_back(DataQueue::CreateInMemoryEntryFromBackingStore( +// store1, 0, len1)); +// list.push_back(DataQueue::CreateInMemoryEntryFromBackingStore( +// store2, 0, len2)); +// +// std::shared_ptr data_queue = +// DataQueue::CreateIdempotent(std::move(list)); +// +// Importantly, idempotent DataQueue's are immutable and all entries +// must be provided when the DataQueue is constructed. Every entry +// must be idempotent with known sizes. The entries may be memory +// resident or streaming. Streaming entries must be capable of +// being read multiple times. +// +// Because idempotent DataQueue's will always produce the same results +// when read, they can be sliced. Slices yield a new DataQueue instance +// that is a subset view over the original: +// +// std::shared_ptr slice = data_queue.slice( +// 5, v8::Just(10UL)); +// +// Example of creating a non-idempotent DataQueue: +// +// std::shared_ptr store1 = getBackingStoreSomehow(); +// std::shared_ptr store2 = getBackingStoreSomehow(); +// +// std::shared_ptr data_queue = DataQueue::Create(); +// +// data_queue->append(DataQueue::CreateInMemoryEntryFromBackingStore( +// store1, 0, len1)); +// +// data_queue->append(DataQueue::CreateInMemoryEntryFromBackingStore( +// store2, 0, len2)); +// +// These data-queues can have new entries appended to them. Entries can +// be memory-resident or streaming. Streaming entries might not have +// a known size. Entries may not be capable of being read multiple +// times. +// +// A non-idempotent data queue will, by default, allow any amount of +// entries to be appended to it. To limit the size of the DataQueue, +// or the close the DataQueue (preventing new entries from being +// appending), use the cap() method. The DataQueue can be capped +// at a specific size or whatever size it currently it. +// +// It might not be possible for a non-idempotent DataQueue to provide +// a size because it might not know how much data a streaming entry +// will ultimately provide. +// +// Non-idempotent DataQueues cannot be sliced. +// +// To read from a DataQueue, we use the node::bob::Source API +// (see src/node_bob.h). +// +// std::shared_ptr reader = data_queue->get_reader(); +// +// reader->Pull( +// [](int status, const DataQueue::Vec* vecs, +// uint64_t count, Done done) { +// // status is one of node::bob::Status +// // vecs is zero or more data buffers containing the read data +// // count is the number of vecs +// // done is a callback to be invoked when done processing the data +// }, options, nullptr, 0, 16); +// +// Keep calling Pull() until status is equal to node::bob::Status::STATUS_EOS. +// +// For idempotent DataQueues, any number of readers can be created and +// pull concurrently from the same DataQueue. The DataQueue can be read +// multiple times. Successful reads should always produce the same result. +// If, for whatever reason, the implementation cannot ensure that the +// data read will remain the same, the read must fail with an error status. +// +// For non-idempotent DataQueues, only a single reader is ever allowed for +// the DataQueue, and the data can only ever be read once. + +class DataQueue : public MemoryRetainer { + public: + struct Vec { + uint8_t* base; + uint64_t len; + }; + + // A DataQueue::Reader consumes the DataQueue. If the data queue is + // idempotent, multiple Readers can be attached to the DataQueue at + // any given time, all guaranteed to yield the same result when the + // data is read. Otherwise, only a single Reader can be attached. + class Reader : public MemoryRetainer, public bob::Source { + public: + using Next = bob::Next; + using Done = bob::Done; + }; + + // A DataQueue::Entry represents a logical chunk of data in the queue. + // The entry may or may not represent memory-resident data. It may + // or may not be consumable more than once. + class Entry : public MemoryRetainer { + public: + // Returns a new Entry that is a view over this entries data + // from the start offset to the ending offset. If the end + // offset is omitted, the slice extends to the end of the + // data. + // + // Creating a slice is only possible if is_idempotent() returns + // true. This is because consuming either the original entry or + // the new entry would change the state of the other in non- + // deterministic ways. When is_idempotent() returns false, slice() + // must return a nulled unique_ptr. + // + // Creating a slice is also only possible if the size of the + // entry is known. If size() returns std::nullopt, slice() + // must return a nulled unique_ptr. + virtual std::unique_ptr slice( + uint64_t start, std::optional end = std::nullopt) = 0; + + // Returns the number of bytes represented by this Entry if it is + // known. Certain types of entries, such as those backed by streams + // might not know the size in advance and therefore cannot provide + // a value. In such cases, size() must return v8::Nothing. + // + // If the entry is idempotent, a size should always be available. + virtual std::optional size() const = 0; + + // When true, multiple reads on the object must produce the exact + // same data or the reads will fail. Some sources of entry data, + // such as streams, may not be capable of preserving idempotency + // and therefore must not claim to be. If an entry claims to be + // idempotent and cannot preserve that quality, subsequent reads + // must fail with an error when a variance is detected. + virtual bool is_idempotent() const = 0; + }; + + // Creates an idempotent DataQueue with a pre-established collection + // of entries. All of the entries must also be idempotent otherwise + // an empty std::unique_ptr will be returned. + static std::shared_ptr CreateIdempotent( + std::vector> list); + + // Creates a non-idempotent DataQueue. This kind of queue can be + // mutated and updated such that multiple reads are not guaranteed + // to produce the same result. The entries added can be of any type. + static std::shared_ptr Create( + std::optional capped = std::nullopt); + + // Creates an idempotent Entry from a v8::ArrayBufferView. To help + // ensure idempotency, the underlying ArrayBuffer is detached from + // the BackingStore. It is the callers responsibility to ensure that + // the BackingStore is not otherwise modified through any other + // means. If the ArrayBuffer is not detachable, nullptr will be + // returned. + static std::unique_ptr CreateInMemoryEntryFromView( + v8::Local view); + + // Creates an idempotent Entry from a v8::BackingStore. It is the + // callers responsibility to ensure that the BackingStore is not + // otherwise modified through any other means. If the ArrayBuffer + // is not detachable, nullptr will be returned. + static std::unique_ptr CreateInMemoryEntryFromBackingStore( + std::shared_ptr store, + uint64_t offset, + uint64_t length); + + static std::unique_ptr CreateDataQueueEntry( + std::shared_ptr data_queue); + + static std::unique_ptr CreateFdEntry(Environment* env, + v8::Local path); + + // Creates a Reader for the given queue. If the queue is idempotent, + // any number of readers can be created, all of which are guaranteed + // to provide the same data. Otherwise, only a single reader is + // permitted. + virtual std::shared_ptr get_reader() = 0; + + // Append a single new entry to the queue. Appending is only allowed + // when is_idempotent() is false. std::nullopt will be returned + // if is_idempotent() is true. std::optional(false) will be returned if the + // data queue is not idempotent but the entry otherwise cannot be added. + virtual std::optional append(std::unique_ptr entry) = 0; + + // Caps the size of this DataQueue preventing additional entries to + // be added if those cause the size to extend beyond the specified + // limit. + // + // If limit is zero, or is less than the known current size of the + // data queue, the limit is set to the current known size, meaning + // that no additional entries can be added at all. + // + // If the size of the data queue is not known, the limit will be + // ignored and no additional entries will be allowed at all. + // + // If is_idempotent is true capping is unnecessary because the data + // queue cannot be appended to. In that case, cap() is a non-op. + // + // If the data queue has already been capped, cap can be called + // again with a smaller size. + virtual void cap(uint64_t limit = 0) = 0; + + // Returns a new DataQueue that is a view over this queues data + // from the start offset to the ending offset. If the end offset + // is omitted, the slice extends to the end of the data. + // + // The slice will coverage a range from start up to, but excluding, end. + // + // Creating a slice is only possible if is_idempotent() returns + // true. This is because consuming either the original DataQueue or + // the new queue would change the state of the other in non- + // deterministic ways. When is_idempotent() returns false, slice() + // must return a nulled unique_ptr. + // + // Creating a slice is also only possible if the size of the + // DataQueue is known. If size() returns std::nullopt, slice() + // must return a null unique_ptr. + virtual std::shared_ptr slice( + uint64_t start, std::optional end = std::nullopt) = 0; + + // The size of DataQueue is the total size of all of its member entries. + // If any of the entries is not able to specify a size, the DataQueue + // will also be incapable of doing so, in which case size() must return + // std::nullopt. + virtual std::optional size() const = 0; + + // A DataQueue is idempotent only if all of its member entries are + // idempotent. + virtual bool is_idempotent() const = 0; + + // True only if cap is called or the data queue is a limited to a + // fixed size. + virtual bool is_capped() const = 0; + + // If the data queue has been capped, and the size of the data queue + // is known, maybeCapRemaining will return the number of additional + // bytes the data queue can receive before reaching the cap limit. + // If the size of the queue cannot be known, or the cap has not + // been set, maybeCapRemaining() will return std::nullopt. + virtual std::optional maybeCapRemaining() const = 0; + + static void Initialize(Environment* env, v8::Local target); + static void RegisterExternalReferences(ExternalReferenceRegistry* registry); +}; + +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/node_contextify.cc b/src/node_contextify.cc index f37e95497c48c9..11553967470043 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -249,7 +249,7 @@ BaseObjectPtr ContextifyContext::New( const ContextOptions& options) { HandleScope scope(env->isolate()); // This only initializes part of the context. The primordials are - // only initilaized when needed because even deserializing them slows + // only initialized when needed because even deserializing them slows // things down significantly and they are only needed in rare occasions // in the vm contexts. if (InitializeContextRuntime(v8_context).IsNothing()) {