Toggle navigation
MeasureThat.net
Create a benchmark
Tools
Feedback
FAQ
Register
Log In
Search: indexOf vs regex
(version: 0)
Comparing performance of:
search_index_cased vs search_index_ignore vs search_regex_cased vs search_regex_ignore
Created:
3 years ago
by:
Guest
Jump to the latest result
Script Preparation code:
const content = `var emitter = require('../emitter');\nvar logger = require('../logger');\nvar ShareDBError = require('../error');\nvar types = require('../types');\nvar util = require('../util');\nvar clone = util.clone;\nvar deepEqual = require('fast-deep-equal');\n\nvar ERROR_CODE = ShareDBError.CODES;\n\n/**\n * A Doc is a client's view on a sharejs document.\n *\n * It is is uniquely identified by its \`id\` and \`collection\`. Documents\n * should not be created directly. Create them with connection.get()\n *\n *\n * Subscriptions\n * -------------\n *\n * We can subscribe a document to stay in sync with the server.\n * doc.subscribe(function(error) {\n * doc.subscribed // = true\n * })\n * The server now sends us all changes concerning this document and these are\n * applied to our data. If the subscription was successful the initial\n * data and version sent by the server are loaded into the document.\n *\n * To stop listening to the changes we call \`doc.unsubscribe()\`.\n *\n * If we just want to load the data but not stay up-to-date, we call\n * doc.fetch(function(error) {\n * doc.data // sent by server\n * })\n *\n *\n * Events\n * ------\n *\n * You can use doc.on(eventName, callback) to subscribe to the following events:\n * - \`before op (op, source)\` Fired before a partial operation is applied to the data.\n * It may be used to read the old data just before applying an operation\n * - \`op (op, source)\` Fired after every partial operation with this operation as the\n * first argument\n * - \`create (source)\` The document was created. That means its type was\n * set and it has some initial data.\n * - \`del (data, source)\` Fired after the document is deleted, that is\n * the data is null. It is passed the data before deletion as an\n * argument\n * - \`load ()\` Fired when a new snapshot is ingested from a fetch, subscribe, or query\n */\n\nmodule.exports = Doc;\nfunction Doc(connection, collection, id) {\n emitter.EventEmitter.call(this);\n\n this.connection = connection;\n\n this.collection = collection;\n this.id = id;\n\n this.version = null;\n this.type = null;\n this.data = undefined;\n\n // Array of callbacks or nulls as placeholders\n this.inflightFetch = [];\n this.inflightSubscribe = null;\n this.pendingFetch = [];\n this.pendingSubscribe = [];\n\n // Whether we think we are subscribed on the server. Synchronously set to\n // false on calls to unsubscribe and disconnect. Should never be true when\n // this.wantSubscribe is false\n this.subscribed = false;\n // Whether to re-establish the subscription on reconnect\n this.wantSubscribe = false;\n\n // The op that is currently roundtripping to the server, or null.\n //\n // When the connection reconnects, the inflight op is resubmitted.\n //\n // This has the same format as an entry in pendingOps\n this.inflightOp = null;\n\n // All ops that are waiting for the server to acknowledge this.inflightOp\n // This used to just be a single operation, but creates & deletes can't be\n // composed with regular operations.\n //\n // This is a list of {[create:{...}], [del:true], [op:...], callbacks:[...]}\n this.pendingOps = [];\n\n // The OT type of this document. An uncreated document has type \`null\`\n this.type = null;\n\n // The applyStack enables us to track any ops submitted while we are\n // applying an op incrementally. This value is an array when we are\n // performing an incremental apply and null otherwise. When it is an array,\n // all submitted ops should be pushed onto it. The \`_otApply\` method will\n // reset it back to null when all incremental apply loops are complete.\n this.applyStack = null;\n\n // Disable the default behavior of composing submitted ops. This is read at\n // the time of op submit, so it may be toggled on before submitting a\n // specifc op and toggled off afterward\n this.preventCompose = false;\n\n // If set to true, the source will be submitted over the connection. This\n // will also have the side-effect of only composing ops whose sources are\n // equal\n this.submitSource = false;\n\n // Prevent own ops being submitted to the server. If subscribed, remote\n // ops are still received. Should be toggled through the pause() and\n // resume() methods to correctly flush on resume.\n this.paused = false;\n\n // Internal counter that gets incremented every time doc.data is updated.\n // Used as a cheap way to check if doc.data has changed.\n this._dataStateVersion = 0;\n}\nemitter.mixin(Doc);\n\nDoc.prototype.destroy = function(callback) {\n var doc = this;\n doc.whenNothingPending(function() {\n if (doc.wantSubscribe) {\n doc.unsubscribe(function(err) {\n if (err) {\n if (callback) return callback(err);\n return doc.emit('error', err);\n }\n doc.connection._destroyDoc(doc);\n doc.emit('destroy');\n if (callback) callback();\n });\n } else {\n doc.connection._destroyDoc(doc);\n doc.emit('destroy');\n if (callback) callback();\n }\n });\n};\n\n\n// ****** Manipulating the document data, version and type.\n\n// Set the document's type, and associated properties. Most of the logic in\n// this function exists to update the document based on any added & removed API\n// methods.\n//\n// @param newType OT type provided by the ottypes library or its name or uri\nDoc.prototype._setType = function(newType) {\n if (typeof newType === 'string') {\n newType = types.map[newType];\n }\n\n if (newType) {\n this.type = newType;\n } else if (newType === null) {\n this.type = newType;\n // If we removed the type from the object, also remove its data\n this._setData(undefined);\n } else {\n var err = new ShareDBError(ERROR_CODE.ERR_DOC_TYPE_NOT_RECOGNIZED, 'Missing type ' + newType);\n return this.emit('error', err);\n }\n};\n\nDoc.prototype._setData = function(data) {\n this.data = data;\n this._dataStateVersion++;\n};\n\n// Ingest snapshot data. This data must include a version, snapshot and type.\n// This is used both to ingest data that was exported with a webpage and data\n// that was received from the server during a fetch.\n//\n// @param snapshot.v version\n// @param snapshot.data\n// @param snapshot.type\n// @param callback\nDoc.prototype.ingestSnapshot = function(snapshot, callback) {\n if (!snapshot) return callback && callback();\n\n if (typeof snapshot.v !== 'number') {\n var err = new ShareDBError(\n ERROR_CODE.ERR_INGESTED_SNAPSHOT_HAS_NO_VERSION,\n 'Missing version in ingested snapshot. ' + this.collection + '.' + this.id\n );\n if (callback) return callback(err);\n return this.emit('error', err);\n }\n\n // If the doc is already created or there are ops pending, we cannot use the\n // ingested snapshot and need ops in order to update the document\n if (this.type || this.hasWritePending()) {\n // The version should only be null on a created document when it was\n // created locally without fetching\n if (this.version == null) {\n if (this.hasWritePending()) {\n // If we have pending ops and we get a snapshot for a locally created\n // document, we have to wait for the pending ops to complete, because\n // we don't know what version to fetch ops from. It is possible that\n // the snapshot came from our local op, but it is also possible that\n // the doc was created remotely (which would conflict and be an error)\n return callback && this.once('no write pending', callback);\n }\n // Otherwise, we've encounted an error state\n var err = new ShareDBError(\n ERROR_CODE.ERR_DOC_MISSING_VERSION,\n 'Cannot ingest snapshot in doc with null version. ' + this.collection + '.' + this.id\n );\n if (callback) return callback(err);\n return this.emit('error', err);\n }\n // If we got a snapshot for a version further along than the document is\n // currently, issue a fetch to get the latest ops and catch us up\n if (snapshot.v > this.version) return this.fetch(callback);\n return callback && callback();\n }\n\n // Ignore the snapshot if we are already at a newer version. Under no\n // circumstance should we ever set the current version backward\n if (this.version > snapshot.v) return callback && callback();\n\n this.version = snapshot.v;\n var type = (snapshot.type === undefined) ? types.defaultType : snapshot.type;\n this._setType(type);\n this._setData(\n (this.type && this.type.deserialize) ?\n this.type.deserialize(snapshot.data) :\n snapshot.data\n );\n this.emit('load');\n callback && callback();\n};\n\nDoc.prototype.whenNothingPending = function(callback) {\n var doc = this;\n util.nextTick(function() {\n if (doc.hasPending()) {\n doc.once('nothing pending', callback);\n return;\n }\n callback();\n });\n};\n\nDoc.prototype.hasPending = function() {\n return !!(\n this.inflightOp ||\n this.pendingOps.length ||\n this.inflightFetch.length ||\n this.inflightSubscribe ||\n this.pendingFetch.length ||\n this.pendingSubscribe.length\n );\n};\n\nDoc.prototype.hasWritePending = function() {\n return !!(this.inflightOp || this.pendingOps.length);\n};\n\nDoc.prototype._emitNothingPending = function() {\n if (this.hasWritePending()) return;\n this.emit('no write pending');\n if (this.hasPending()) return;\n this.emit('nothing pending');\n};\n\n// **** Helpers for network messages\n\nDoc.prototype._emitResponseError = function(err, callback) {\n if (err && err.code === ERROR_CODE.ERR_SNAPSHOT_READ_SILENT_REJECTION) {\n this.wantSubscribe = false;\n if (callback) {\n callback();\n }\n this._emitNothingPending();\n return;\n }\n if (callback) {\n callback(err);\n this._emitNothingPending();\n return;\n }\n this._emitNothingPending();\n this.emit('error', err);\n};\n\nDoc.prototype._handleFetch = function(error, snapshot) {\n var callbacks = this.pendingFetch;\n this.pendingFetch = [];\n var callback = this.inflightFetch.shift();\n if (callback) callbacks.push(callback);\n if (callbacks.length) {\n callback = function(error) {\n util.callEach(callbacks, error);\n };\n }\n if (error) return this._emitResponseError(error, callback);\n this.ingestSnapshot(snapshot, callback);\n this._emitNothingPending();\n};\n\nDoc.prototype._handleSubscribe = function(error, snapshot) {\n var request = this.inflightSubscribe;\n this.inflightSubscribe = null;\n var callbacks = this.pendingFetch;\n this.pendingFetch = [];\n if (request.callback) callbacks.push(request.callback);\n var callback;\n if (callbacks.length) {\n callback = function(error) {\n util.callEach(callbacks, error);\n };\n }\n if (error) return this._emitResponseError(error, callback);\n this.subscribed = request.wantSubscribe;\n if (this.subscribed) this.ingestSnapshot(snapshot, callback);\n else if (callback) callback();\n this._emitNothingPending();\n this._flushSubscribe();\n};\n\nDoc.prototype._handleOp = function(err, message) {\n if (err) {\n if (this.inflightOp) {\n // The server has rejected submission of the current operation. If we get\n // an "Op submit rejected" error, this was done intentionally\n // and we should roll back but not return an error to the user.\n if (err.code === ERROR_CODE.ERR_OP_SUBMIT_REJECTED) err = null;\n return this._rollback(err);\n }\n return this.emit('error', err);\n }\n\n if (this.inflightOp &&\n message.src === this.inflightOp.src &&\n message.seq === this.inflightOp.seq) {\n // The op has already been applied locally. Just update the version\n // and pending state appropriately\n this._opAcknowledged(message);\n return;\n }\n\n if (this.version == null || message.v > this.version) {\n // This will happen in normal operation if we become subscribed to a\n // new document via a query. It can also happen if we get an op for\n // a future version beyond the version we are expecting next. This\n // could happen if the server doesn't publish an op for whatever reason\n // or because of a race condition. In any case, we can send a fetch\n // command to catch back up.\n //\n // Fetch only sends a new fetch command if no fetches are inflight, which\n // will act as a natural debouncing so we don't send multiple fetch\n // requests for many ops received at once.\n this.fetch();\n return;\n }\n\n if (message.v < this.version) {\n // We can safely ignore the old (duplicate) operation.\n return;\n }\n\n if (this.inflightOp) {\n var transformErr = transformX(this.inflightOp, message);\n if (transformErr) return this._hardRollback(transformErr);\n }\n\n for (var i = 0; i < this.pendingOps.length; i++) {\n var transformErr = transformX(this.pendingOps[i], message);\n if (transformErr) return this._hardRollback(transformErr);\n }\n\n this.version++;\n try {\n this._otApply(message, false);\n } catch (error) {\n return this._hardRollback(error);\n }\n};\n\n// Called whenever (you guessed it!) the connection state changes. This will\n// happen when we get disconnected & reconnect.\nDoc.prototype._onConnectionStateChanged = function() {\n if (this.connection.canSend) {\n this.flush();\n this._resubscribe();\n } else {\n if (this.inflightOp) {\n this.pendingOps.unshift(this.inflightOp);\n this.inflightOp = null;\n }\n this.subscribed = false;\n if (this.inflightSubscribe) {\n if (this.inflightSubscribe.wantSubscribe) {\n this.pendingSubscribe.unshift(this.inflightSubscribe);\n this.inflightSubscribe = null;\n } else {\n this._handleSubscribe();\n }\n }\n if (this.inflightFetch.length) {\n this.pendingFetch = this.pendingFetch.concat(this.inflightFetch);\n this.inflightFetch.length = 0;\n }\n }\n};\n\nDoc.prototype._resubscribe = function() {\n if (!this.pendingSubscribe.length && this.wantSubscribe) {\n return this.subscribe();\n }\n var willFetch = this.pendingSubscribe.some(function(request) {\n return request.wantSubscribe;\n });\n if (!willFetch && this.pendingFetch.length) this.fetch();\n this._flushSubscribe();\n};\n\n// Request the current document snapshot or ops that bring us up to date\nDoc.prototype.fetch = function(callback) {\n if (this.connection.canSend) {\n var isDuplicate = this.connection.sendFetch(this);\n pushActionCallback(this.inflightFetch, isDuplicate, callback);\n return;\n }\n this.pendingFetch.push(callback);\n};\n\n// Fetch the initial document and keep receiving updates\nDoc.prototype.subscribe = function(callback) {\n var wantSubscribe = true;\n this._queueSubscribe(wantSubscribe, callback);\n};\n\n// Unsubscribe. The data will stay around in local memory, but we'll stop\n// receiving updates\nDoc.prototype.unsubscribe = function(callback) {\n var wantSubscribe = false;\n this._queueSubscribe(wantSubscribe, callback);\n};\n\nDoc.prototype._queueSubscribe = function(wantSubscribe, callback) {\n var lastRequest = this.pendingSubscribe[this.pendingSubscribe.length - 1] || this.inflightSubscribe;\n var isDuplicateRequest = lastRequest && lastRequest.wantSubscribe === wantSubscribe;\n if (isDuplicateRequest) {\n lastRequest.callback = combineCallbacks([lastRequest.callback, callback]);\n return;\n }\n this.pendingSubscribe.push({\n wantSubscribe: !!wantSubscribe,\n callback: callback\n });\n this._flushSubscribe();\n};\n\nDoc.prototype._flushSubscribe = function() {\n if (this.inflightSubscribe || !this.pendingSubscribe.length) return;\n\n if (this.connection.canSend) {\n this.inflightSubscribe = this.pendingSubscribe.shift();\n this.wantSubscribe = this.inflightSubscribe.wantSubscribe;\n if (this.wantSubscribe) {\n this.connection.sendSubscribe(this);\n } else {\n // Be conservative about our subscription state. We'll be unsubscribed\n // some time between sending this request, and receiving the callback,\n // so let's just set ourselves to unsubscribed now.\n this.subscribed = false;\n this.connection.sendUnsubscribe(this);\n }\n\n return;\n }\n\n // If we're offline, then we're already unsubscribed. Therefore, call back\n // the next request immediately if it's an unsubscribe request.\n if (!this.pendingSubscribe[0].wantSubscribe) {\n this.inflightSubscribe = this.pendingSubscribe.shift();\n var doc = this;\n util.nextTick(function() {\n doc._handleSubscribe();\n });\n }\n};\n\nfunction pushActionCallback(inflight, isDuplicate, callback) {\n if (isDuplicate) {\n var lastCallback = inflight.pop();\n inflight.push(function(err) {\n lastCallback && lastCallback(err);\n callback && callback(err);\n });\n } else {\n inflight.push(callback);\n }\n}\n\nfunction combineCallbacks(callbacks) {\n callbacks = callbacks.filter(util.truthy);\n if (!callbacks.length) return null;\n return function(error) {\n util.callEach(callbacks, error);\n };\n}\n\n\n// Operations //\n\n// Send the next pending op to the server, if we can.\n//\n// Only one operation can be in-flight at a time. If an operation is already on\n// its way, or we're not currently connected, this method does nothing.\nDoc.prototype.flush = function() {\n // Ignore if we can't send or we are already sending an op\n if (!this.connection.canSend || this.inflightOp) return;\n\n // Send first pending op unless paused\n if (!this.paused && this.pendingOps.length) {\n this._sendOp();\n }\n};\n\n// Helper function to set op to contain a no-op.\nfunction setNoOp(op) {\n delete op.op;\n delete op.create;\n delete op.del;\n}\n\n// Transform server op data by a client op, and vice versa. Ops are edited in place.\nfunction transformX(client, server) {\n // Order of statements in this function matters. Be especially careful if\n // refactoring this function\n\n // A client delete op should dominate if both the server and the client\n // delete the document. Thus, any ops following the client delete (such as a\n // subsequent create) will be maintained, since the server op is transformed\n // to a no-op\n if (client.del) return setNoOp(server);\n\n if (server.del) {\n return new ShareDBError(ERROR_CODE.ERR_DOC_WAS_DELETED, 'Document was deleted');\n }\n if (server.create) {\n return new ShareDBError(ERROR_CODE.ERR_DOC_ALREADY_CREATED, 'Document already created');\n }\n\n // Ignore no-op coming from server\n if (!('op' in server)) return;\n\n // I believe that this should not occur, but check just in case\n if (client.create) {\n return new ShareDBError(ERROR_CODE.ERR_DOC_ALREADY_CREATED, 'Document already created');\n }\n\n // They both edited the document. This is the normal case for this function -\n // as in, most of the time we'll end up down here.\n //\n // You should be wondering why I'm using client.type instead of this.type.\n // The reason is, if we get ops at an old version of the document, this.type\n // might be undefined or a totally different type. By pinning the type to the\n // op data, we make sure the right type has its transform function called.\n if (client.type.transformX) {\n var result = client.type.transformX(client.op, server.op);\n client.op = result[0];\n server.op = result[1];\n } else {\n var clientOp = client.type.transform(client.op, server.op, 'left');\n var serverOp = client.type.transform(server.op, client.op, 'right');\n client.op = clientOp;\n server.op = serverOp;\n }\n};\n\n/**\n * Applies the operation to the snapshot\n *\n * If the operation is create or delete it emits \`create\` or \`del\`. Then the\n * operation is applied to the snapshot and \`op\` and \`after op\` are emitted.\n * If the type supports incremental updates and \`this.incremental\` is true we\n * fire \`op\` after every small operation.\n *\n * This is the only function to fire the above mentioned events.\n *\n * @private\n */\nDoc.prototype._otApply = function(op, source) {\n if ('op' in op) {\n if (!this.type) {\n // Throw here, because all usage of _otApply should be wrapped with a try/catch\n throw new ShareDBError(\n ERROR_CODE.ERR_DOC_DOES_NOT_EXIST,\n 'Cannot apply op to uncreated document. ' + this.collection + '.' + this.id\n );\n }\n\n // NB: If we need to add another argument to this event, we should consider\n // the fact that the 'op' event has op.src as its 3rd argument\n this.emit('before op batch', op.op, source);\n\n // Iteratively apply multi-component remote operations and rollback ops\n // (source === false) for the default JSON0 OT type. It could use\n // type.shatter(), but since this code is so specific to use cases for the\n // JSON0 type and ShareDB explicitly bundles the default type, we might as\n // well write it this way and save needing to iterate through the op\n // components twice.\n //\n // Ideally, we would not need this extra complexity. However, it is\n // helpful for implementing bindings that update DOM nodes and other\n // stateful objects by translating op events directly into corresponding\n // mutations. Such bindings are most easily written as responding to\n // individual op components one at a time in order, and it is important\n // that the snapshot only include updates from the particular op component\n // at the time of emission. Eliminating this would require rethinking how\n // such external bindings are implemented.\n if (!source && this.type === types.defaultType && op.op.length > 1) {\n if (!this.applyStack) this.applyStack = [];\n var stackLength = this.applyStack.length;\n for (var i = 0; i < op.op.length; i++) {\n var component = op.op[i];\n var componentOp = {op: [component]};\n // Apply the individual op component\n this.emit('before op', componentOp.op, source, op.src);\n // Transform componentOp against any ops that have been submitted\n // sychronously inside of an op event handler since we began apply of\n // our operation\n for (var j = stackLength; j < this.applyStack.length; j++) {\n var transformErr = transformX(this.applyStack[j], componentOp);\n if (transformErr) return this._hardRollback(transformErr);\n }\n this._setData(this.type.apply(this.data, componentOp.op));\n this.emit('op', componentOp.op, source, op.src);\n }\n this.emit('op batch', op.op, source);\n // Pop whatever was submitted since we started applying this op\n this._popApplyStack(stackLength);\n return;\n }\n\n // The 'before op' event enables clients to pull any necessary data out of\n // the snapshot before it gets changed\n this.emit('before op', op.op, source, op.src);\n // Apply the operation to the local data, mutating it in place\n this._setData(this.type.apply(this.data, op.op));\n // Emit an 'op' event once the local data includes the changes from the\n // op. For locally submitted ops, this will be synchronously with\n // submission and before the server or other clients have received the op.\n // For ops from other clients, this will be after the op has been\n // committed to the database and published\n this.emit('op', op.op, source, op.src);\n this.emit('op batch', op.op, source);\n return;\n }\n\n if (op.create) {\n this._setType(op.create.type);\n if (this.type.deserialize) {\n if (this.type.createDeserialized) {\n this._setData(this.type.createDeserialized(op.create.data));\n } else {\n this._setData(this.type.deserialize(this.type.create(op.create.data)));\n }\n } else {\n this._setData(this.type.create(op.create.data));\n }\n this.emit('create', source);\n return;\n }\n\n if (op.del) {\n var oldData = this.data;\n this._setType(null);\n this.emit('del', oldData, source);\n return;\n }\n};\n\n\n// ***** Sending operations\n\n// Actually send op to the server.\nDoc.prototype._sendOp = function() {\n if (!this.connection.canSend) return;\n var src = this.connection.id;\n\n // When there is no inflightOp, send the first item in pendingOps. If\n // there is inflightOp, try sending it again\n if (!this.inflightOp) {\n // Send first pending op\n this.inflightOp = this.pendingOps.shift();\n }\n var op = this.inflightOp;\n if (!op) {\n var err = new ShareDBError(ERROR_CODE.ERR_INFLIGHT_OP_MISSING, 'No op to send on call to _sendOp');\n return this.emit('error', err);\n }\n\n // Track data for retrying ops\n op.sentAt = Date.now();\n op.retries = (op.retries == null) ? 0 : op.retries + 1;\n\n // The src + seq number is a unique ID representing this operation. This tuple\n // is used on the server to detect when ops have been sent multiple times and\n // on the client to match acknowledgement of an op back to the inflightOp.\n // Note that the src could be different from this.connection.id after a\n // reconnect, since an op may still be pending after the reconnection and\n // this.connection.id will change. In case an op is sent multiple times, we\n // also need to be careful not to override the original seq value.\n if (op.seq == null) {\n if (this.connection.seq >= util.MAX_SAFE_INTEGER) {\n return this.emit('error', new ShareDBError(\n ERROR_CODE.ERR_CONNECTION_SEQ_INTEGER_OVERFLOW,\n 'Connection seq has exceeded the max safe integer, maybe from being open for too long'\n ));\n }\n\n op.seq = this.connection.seq++;\n }\n\n this.connection.sendOp(this, op);\n\n // src isn't needed on the first try, since the server session will have the\n // same id, but it must be set on the inflightOp in case it is sent again\n // after a reconnect and the connection's id has changed by then\n if (op.src == null) op.src = src;\n};\n\n\n// Queues the operation for submission to the server and applies it locally.\n//\n// Internal method called to do the actual work for submit(), create() and del().\n// @private\n//\n// @param op\n// @param [op.op]\n// @param [op.del]\n// @param [op.create]\n// @param [callback] called when operation is submitted\nDoc.prototype._submit = function(op, source, callback) {\n // Locally submitted ops must always have a truthy source\n if (!source) source = true;\n\n // The op contains either op, create, delete, or none of the above (a no-op).\n if ('op' in op) {\n if (!this.type) {\n var err = new ShareDBError(\n ERROR_CODE.ERR_DOC_DOES_NOT_EXIST,\n 'Cannot submit op. Document has not been created. ' + this.collection + '.' + this.id\n );\n if (callback) return callback(err);\n return this.emit('error', err);\n }\n // Try to normalize the op. This removes trailing skip:0's and things like that.\n if (this.type.normalize) op.op = this.type.normalize(op.op);\n }\n\n try {\n this._pushOp(op, source, callback);\n this._otApply(op, source);\n } catch (error) {\n return this._hardRollback(error);\n }\n\n // The call to flush is delayed so if submit() is called multiple times\n // synchronously, all the ops are combined before being sent to the server.\n var doc = this;\n util.nextTick(function() {\n doc.flush();\n });\n};\n\nDoc.prototype._pushOp = function(op, source, callback) {\n op.source = source;\n if (this.applyStack) {\n // If we are in the process of incrementally applying an operation, don't\n // compose the op and push it onto the applyStack so it can be transformed\n // against other components from the op or ops being applied\n this.applyStack.push(op);\n } else {\n // If the type supports composes, try to compose the operation onto the\n // end of the last pending operation.\n var composed = this._tryCompose(op);\n if (composed) {\n composed.callbacks.push(callback);\n return;\n }\n }\n // Push on to the pendingOps queue of ops to submit if we didn't compose\n op.type = this.type;\n op.callbacks = [callback];\n this.pendingOps.push(op);\n};\n\nDoc.prototype._popApplyStack = function(to) {\n if (to > 0) {\n this.applyStack.length = to;\n return;\n }\n // Once we have completed the outermost apply loop, reset to null and no\n // longer add ops to the applyStack as they are submitted\n var op = this.applyStack[0];\n this.applyStack = null;\n if (!op) return;\n // Compose the ops added since the beginning of the apply stack, since we\n // had to skip compose when they were originally pushed\n var i = this.pendingOps.indexOf(op);\n if (i === -1) return;\n var ops = this.pendingOps.splice(i);\n for (var i = 0; i < ops.length; i++) {\n var op = ops[i];\n var composed = this._tryCompose(op);\n if (composed) {\n composed.callbacks = composed.callbacks.concat(op.callbacks);\n } else {\n this.pendingOps.push(op);\n }\n }\n};\n\n// Try to compose a submitted op into the last pending op. Returns the\n// composed op if it succeeds, undefined otherwise\nDoc.prototype._tryCompose = function(op) {\n if (this.preventCompose) return;\n\n // We can only compose into the last pending op. Inflight ops have already\n // been sent to the server, so we can't modify them\n var last = this.pendingOps[this.pendingOps.length - 1];\n if (!last || last.sentAt) return;\n\n // If we're submitting the op source, we can only combine ops that have\n // a matching source\n if (this.submitSource && !deepEqual(op.source, last.source)) return;\n\n // Compose an op into a create by applying it. This effectively makes the op\n // invisible, as if the document were created including the op originally\n if (last.create && 'op' in op) {\n last.create.data = this.type.apply(last.create.data, op.op);\n return last;\n }\n\n // Compose two ops into a single op if supported by the type. Types that\n // support compose must be able to compose any two ops together\n if ('op' in last && 'op' in op && this.type.compose) {\n last.op = this.type.compose(last.op, op.op);\n return last;\n }\n};\n\n// *** Client OT entrypoints.\n\n// Submit an operation to the document.\n//\n// @param operation handled by the OT type\n// @param options {source: ...}\n// @param [callback] called after operation submitted\n//\n// @fires before op, op, after op\nDoc.prototype.submitOp = function(component, options, callback) {\n if (typeof options === 'function') {\n callback = options;\n options = null;\n }\n var op = {op: component};\n var source = options && options.source;\n this._submit(op, source, callback);\n};\n\n// Create the document, which in ShareJS semantics means to set its type. Every\n// object implicitly exists in the database but has no data and no type. Create\n// sets the type of the object and can optionally set some initial data on the\n// object, depending on the type.\n//\n// @param data initial\n// @param type OT type\n// @param options {source: ...}\n// @param callback called when operation submitted\nDoc.prototype.create = function(data, type, options, callback) {\n if (typeof type === 'function') {\n callback = type;\n options = null;\n type = null;\n } else if (typeof options === 'function') {\n callback = options;\n options = null;\n }\n if (!type) {\n type = types.defaultType.uri;\n }\n if (this.type) {\n var err = new ShareDBError(ERROR_CODE.ERR_DOC_ALREADY_CREATED, 'Document already exists');\n if (callback) return callback(err);\n return this.emit('error', err);\n }\n var op = {create: {type: type, data: data}};\n var source = options && options.source;\n this._submit(op, source, callback);\n};\n\n// Delete the document. This creates and submits a delete operation to the\n// server. Deleting resets the object's type to null and deletes its data. The\n// document still exists, and still has the version it used to have before you\n// deleted it (well, old version +1).\n//\n// @param options {source: ...}\n// @param callback called when operation submitted\nDoc.prototype.del = function(options, callback) {\n if (typeof options === 'function') {\n callback = options;\n options = null;\n }\n if (!this.type) {\n var err = new ShareDBError(ERROR_CODE.ERR_DOC_DOES_NOT_EXIST, 'Document does not exist');\n if (callback) return callback(err);\n return this.emit('error', err);\n }\n var op = {del: true};\n var source = options && options.source;\n this._submit(op, source, callback);\n};\n\n\n// Stops the document from sending any operations to the server.\nDoc.prototype.pause = function() {\n this.paused = true;\n};\n\n// Continue sending operations to the server\nDoc.prototype.resume = function() {\n this.paused = false;\n this.flush();\n};\n\n// Create a snapshot that can be serialized, deserialized, and passed into \`Doc.ingestSnapshot\`.\nDoc.prototype.toSnapshot = function() {\n return {\n v: this.version,\n data: clone(this.data),\n type: this.type.uri\n };\n};\n\n// *** Receiving operations\n\n// This is called when the server acknowledges an operation from the client.\nDoc.prototype._opAcknowledged = function(message) {\n if (this.inflightOp.create) {\n this.version = message.v;\n } else if (message.v !== this.version) {\n // We should already be at the same version, because the server should\n // have sent all the ops that have happened before acknowledging our op\n logger.warn('Invalid version from server. Expected: ' + this.version + ' Received: ' + message.v, message);\n\n // Fetching should get us back to a working document state\n return this.fetch();\n }\n\n // The op was committed successfully. Increment the version number\n this.version++;\n\n this._clearInflightOp();\n};\n\nDoc.prototype._rollback = function(err) {\n // The server has rejected submission of the current operation. Invert by\n // just the inflight op if possible. If not possible to invert, cancel all\n // pending ops and fetch the latest from the server to get us back into a\n // working state, then call back\n var op = this.inflightOp;\n\n if ('op' in op && op.type.invert) {\n try {\n op.op = op.type.invert(op.op);\n } catch (error) {\n // If the op doesn't support \`.invert()\`, we just reload the doc\n // instead of trying to locally revert it.\n return this._hardRollback(err);\n }\n\n // Transform the undo operation by any pending ops.\n for (var i = 0; i < this.pendingOps.length; i++) {\n var transformErr = transformX(this.pendingOps[i], op);\n if (transformErr) return this._hardRollback(transformErr);\n }\n\n // ... and apply it locally, reverting the changes.\n //\n // This operation is applied to look like it comes from a remote source.\n // I'm still not 100% sure about this functionality, because its really a\n // local op. Basically, the problem is that if the client's op is rejected\n // by the server, the editor window should update to reflect the undo.\n try {\n this._otApply(op, false);\n } catch (error) {\n return this._hardRollback(error);\n }\n\n this._clearInflightOp(err);\n return;\n }\n\n this._hardRollback(err);\n};\n\nDoc.prototype._hardRollback = function(err) {\n // Store pending ops so that we can notify their callbacks of the error.\n // We combine the inflight op and the pending ops, because it's possible\n // to hit a condition where we have no inflight op, but we do have pending\n // ops. This can happen when an invalid op is submitted, which causes us\n // to hard rollback before the pending op was flushed.\n var pendingOps = [];\n if (this.inflightOp) pendingOps.push(this.inflightOp);\n pendingOps = pendingOps.concat(this.pendingOps);\n\n // Cancel all pending ops and reset if we can't invert\n this._setType(null);\n this.version = null;\n this.inflightOp = null;\n this.pendingOps = [];\n\n // Fetch the latest version from the server to get us back into a working state\n var doc = this;\n this.fetch(function() {\n // We want to check that no errors are swallowed, so we check that:\n // - there are callbacks to call, and\n // - that every single pending op called a callback\n // If there are no ops queued, or one of them didn't handle the error,\n // then we emit the error.\n var allOpsHadCallbacks = !!pendingOps.length;\n for (var i = 0; i < pendingOps.length; i++) {\n allOpsHadCallbacks = util.callEach(pendingOps[i].callbacks, err) && allOpsHadCallbacks;\n }\n if (err && !allOpsHadCallbacks) return doc.emit('error', err);\n });\n};\n\nDoc.prototype._clearInflightOp = function(err) {\n var inflightOp = this.inflightOp;\n\n this.inflightOp = null;\n\n var called = util.callEach(inflightOp.callbacks, err);\n\n this.flush();\n this._emitNothingPending();\n\n if (err && !called) return this.emit('error', err);\n};\n`; const query = 'pendingOps'; const query_ignore = query.toLowerCase(); const query_cased = query; const regex_ignore = new RegExp(query, 'gi'); const regex_cased = new RegExp(query, 'g'); function* search_index_cased() { const a = content; const len = query_cased.length; let i = 0; while (true) { i = a.indexOf(query_cased, i + 1); if (i < 0) return; yield [i, len]; } } function* search_index_ignore() { const a = content.toLowerCase(); const len = query_ignore.length; let i = 0; while (true) { i = a.indexOf(query_ignore, i + 1); if (i < 0) return; yield [i, len]; } } function* search_regex_cased() { for (const match of content.matchAll(regex_cased)) { yield [match.index, match[0].length]; } } function* search_regex_ignore() { for (const match of content.matchAll(regex_ignore)) { yield [match.index, match[0].length]; } }
Tests:
search_index_cased
Array.from(search_index_cased())
search_index_ignore
Array.from(search_index_ignore())
search_regex_cased
Array.from(search_regex_cased())
search_regex_ignore
Array.from(search_regex_ignore())
Rendered benchmark preparation results:
Suite status:
<idle, ready to run>
Run tests (4)
Previous results
Fork
Test case name
Result
search_index_cased
search_index_ignore
search_regex_cased
search_regex_ignore
Fastest:
N/A
Slowest:
N/A
Latest run results:
No previous run results
This benchmark does not have any results yet. Be the first one
to run it!
Autogenerated LLM Summary
(model
llama3.2:3b
, generated one year ago):
I'll do my best to analyze the provided code snippet and benchmark results. **Code Analysis** The provided code appears to be part of a JavaScript library, specifically the `Doc` class, which seems to be related to indexing or searching data. The code snippet shows several functions: 1. `_searchIndexCased`: Generates indices for a given query in the `content` string using a cased search (case-sensitive). 2. `_searchIndexIgnore`: Generates indices for a given query in the `content` string, but converts the content to lowercase before searching, making it case-insensitive. 3. `_searchRegexCased`: Uses a regular expression to find matches in the `content` string and returns their indices and lengths. 4. `_searchRegexIgnore`: Similar to `_searchRegexCased`, but uses a case-insensitive regular expression. The code also includes benchmarking information, such as the number of executions per second (EPS) for each test. **Benchmark Results** The provided benchmark results show the EPS values for each test case: | Test Name | Executions Per Second | | --- | --- | | search_index_cased | 122735.5546875 | | search_regex_cased | 106069.234375 | | search_regex_ignore | 73381.8203125 | | search_index_ignore | 27876.806640625 | **Observations** * The `search_index_cased` test has the highest EPS value, indicating that it is the fastest. * The `search_regex_cased` and `search_regex_ignore` tests have relatively high EPS values, suggesting they are also fast but slightly slower than the cased search. * The `search_index_ignore` and `search_regex_ignore` tests have lower EPS values, indicating that they are slower. **Conclusion** Based on the provided code snippet and benchmark results, it appears that: * Cased searches (e.g., `_searchIndexCased`) are generally faster than case-insensitive searches (e.g., `_searchIndexIgnore`). * Regular expression-based searches (e.g., `_searchRegexCased` and `_searchRegexIgnore`) are slower than cased string searches but still relatively fast. Please note that this analysis is based on the provided code snippet and benchmark results, which may not be representative of the full functionality or performance characteristics of the `Doc` class.
Related benchmarks:
Object Lookup
Empty vs check
(Draft) indexOf vs Regex
(Draft 1) indexOf vs Regex
Comments
Confirm delete:
Do you really want to delete benchmark?