Skip to content

Instantly share code, notes, and snippets.

@ry
Created October 6, 2010 21:57
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ry/614169 to your computer and use it in GitHub Desktop.
Save ry/614169 to your computer and use it in GitHub Desktop.
From 9241b751f9b42d970844fdc62fe84a01aa6d5203 Mon Sep 17 00:00:00 2001
From: Ryan Dahl <ry@tinyclouds.org>
Date: Wed, 6 Oct 2010 14:57:46 -0700
Subject: [PATCH] Raw string access
---
include/v8.h | 26 +++++++++
src/api.cc | 12 ++++
src/objects.cc | 65 ++++++++++++++++++++++
src/objects.h | 5 ++
test/cctest/test-api.cc | 136 +++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 244 insertions(+), 0 deletions(-)
diff --git a/include/v8.h b/include/v8.h
index 9ee1687..81cbc0f 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -1207,6 +1207,32 @@ class String : public Primitive {
*/
V8EXPORT bool CanMakeExternal();
+
+ struct Pointer {
+ const char* data;
+ int length; // in bytes
+ enum { OneByte, TwoByte } chartype;
+ };
+
+ /**
+ * Fills the suppied vectors, pointerv, with the addresses of the string
+ * in the V8 heap. This is intended to be used to immediately flush the
+ * data to socket. These pointers will become invalid the next time V8 is
+ * entered, so use this function with exterme care.
+ *
+ * \param offset the number of characters into the string to start
+ * \param pointerv an empty array of pointer vectors to be filled with data.
+ * \param pointerv_size the size of the passed array.
+ * \param pointerv_filled will be set with the number of vectored filled
+ * after the operation.
+ * \returns the number of characters of the string represented in the
+ * pointers
+ */
+ V8EXPORT int Pointers(int offset,
+ Pointer *pointerv,
+ int pointerv_size,
+ int* pointerv_filled);
+
/** Creates an undetectable string from the supplied ascii or utf-8 data.*/
V8EXPORT static Local<String> NewUndetectable(const char* data,
int length = -1);
diff --git a/src/api.cc b/src/api.cc
index 7ca9e8f..29e4db5 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -3686,6 +3686,18 @@ bool v8::String::CanMakeExternal() {
}
+int v8::String::Pointers(int offset,
+ Pointer *pointerv,
+ int pointerv_size,
+ int* pointerv_filled) {
+ if (IsDeadCheck("v8::String::Pointers()")) return false;
+ i::Handle<i::String> obj = Utils::OpenHandle(this);
+
+ *pointerv_filled = 0;
+ return (*obj)->Pointers(offset, pointerv, pointerv_size, pointerv_filled);
+}
+
+
Local<v8::Object> v8::Object::New() {
EnsureInitialized("v8::Object::New()");
LOG_API("Object::New");
diff --git a/src/objects.cc b/src/objects.cc
index 1a3dc4e..c9b6e1c 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -3987,6 +3987,71 @@ int String::Utf8Length() {
}
+int String::Pointers(int offset,
+ v8::String::Pointer *pointerv,
+ int pointerv_size,
+ int* pointerv_filled) {
+ if (this->length() == 0) return 0;
+ if (*pointerv_filled >= pointerv_size) return 0;
+
+ int chars = 0;
+ const char *start;
+
+ switch (StringShape(this).representation_tag()) {
+ case kConsStringTag: {
+ // TODO: keep track of how far we recurse, if it's very deep, pull out
+ // and flatten.
+ ConsString* cons = ConsString::cast(this);
+ String* first = cons->first();
+ String* second = cons->second();
+
+ if (offset < first->length() && first->length()) {
+ // Need to take the first into consideration.
+ chars += first->Pointers(offset, pointerv, pointerv_size, pointerv_filled);
+ if (*pointerv_filled >= pointerv_size) return chars;
+ offset = 0;
+ } else {
+ offset -= first->length();
+ }
+
+ chars += second->Pointers(offset, pointerv, pointerv_size, pointerv_filled);
+ return chars;
+ }
+
+ case kSeqStringTag: {
+ start = this->IsTwoByteRepresentation()
+ ? reinterpret_cast<char*>(SeqTwoByteString::cast(this)->GetChars())
+ : SeqAsciiString::cast(this)->GetChars();
+ break;
+ }
+
+ case kExternalStringTag: {
+ start = this->IsTwoByteRepresentation()
+ ? (char*)(ExternalTwoByteString::cast(this)->resource()->data())
+ : ExternalAsciiString::cast(this)->resource()->data();
+ break;
+ }
+
+ default:
+ ASSERT(0);
+ *pointerv_filled = 0;
+ return 0;
+ }
+
+ if (this->IsTwoByteRepresentation()) {
+ pointerv[*pointerv_filled].data = start + (2 * offset);
+ pointerv[*pointerv_filled].length = 2 * (this->length() - offset);
+ pointerv[*pointerv_filled].chartype = v8::String::Pointer::TwoByte;
+ } else {
+ pointerv[*pointerv_filled].data = start + offset;
+ pointerv[*pointerv_filled].length = this->length() - offset;
+ pointerv[*pointerv_filled].chartype = v8::String::Pointer::OneByte;
+ }
+ *pointerv_filled += 1;
+ return this->length() - offset;
+}
+
+
Vector<const char> String::ToAsciiVector() {
ASSERT(IsAsciiRepresentation());
ASSERT(IsFlat());
diff --git a/src/objects.h b/src/objects.h
index e284454..b0e25fd 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -4510,6 +4510,11 @@ class String: public HeapObject {
Vector<const char> ToAsciiVector();
Vector<const uc16> ToUC16Vector();
+ int Pointers(int offset,
+ v8::String::Pointer *pointerv,
+ int pointerv_size,
+ int* pointerv_filled);
+
// Mark the string as an undetectable object. It only applies to
// ascii and two byte string types.
bool MarkAsUndetectable();
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index 0631351..72136c1 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -743,6 +743,142 @@ THREADED_TEST(StringConcat) {
}
+THREADED_TEST(StringPointersFlat) {
+ {
+ v8::HandleScope scope;
+ LocalContext env;
+ const char* hello_world = "hello world";
+ Local<String> s = v8_str(hello_world);
+
+ String::Pointer ptrs[2];
+ int used;
+
+ int chars = s->Pointers(0, ptrs, 2, &used);
+
+ CHECK_EQ(1, used);
+ CHECK_EQ(11, ptrs[0].length);
+ CHECK_EQ(11, chars);
+ CHECK_EQ('h', ptrs[0].data[0]);
+ CHECK_EQ('e', ptrs[0].data[1]);
+ CHECK_EQ('l', ptrs[0].data[2]);
+ CHECK_EQ('l', ptrs[0].data[3]);
+ CHECK_EQ('o', ptrs[0].data[4]);
+ CHECK_EQ(' ', ptrs[0].data[5]);
+ CHECK_EQ('w', ptrs[0].data[6]);
+ CHECK_EQ('o', ptrs[0].data[7]);
+ CHECK_EQ('r', ptrs[0].data[8]);
+ CHECK_EQ('l', ptrs[0].data[9]);
+ CHECK_EQ('d', ptrs[0].data[10]);
+ CHECK_EQ(v8::String::Pointer::OneByte, ptrs[0].chartype);
+ }
+ i::CompilationCache::Clear();
+ i::Heap::CollectAllGarbage(false);
+ i::Heap::CollectAllGarbage(false);
+}
+
+
+THREADED_TEST(StringPointersCons) {
+ {
+ v8::HandleScope scope;
+ LocalContext env;
+
+ const char* hello = "hello hello hello hello hello hello ";
+ const char* world = "world world world world world world world world ";
+
+
+ Local<String> s = v8_str(hello);
+ Local<String> t = v8_str(world);
+
+ Local<String> c = v8::String::Concat(s,t);
+
+ // This isn't part of the actual test. Just want to make sure that this
+ // is actually a cons string. If the following test fails, maybe
+ // increase the size of s and t.
+ i::Handle<i::String> str = v8::Utils::OpenHandle(*c);
+ CHECK(i::StringShape(*str).IsCons());
+
+
+ String::Pointer ptrs[3];
+ int used;
+
+ int chars = c->Pointers(0, ptrs, 3, &used);
+
+ CHECK_EQ(2, used);
+ CHECK_EQ(strlen(hello) + strlen(world), chars);
+
+ CHECK_EQ(strlen(hello), ptrs[0].length);
+ CHECK_EQ(v8::String::Pointer::OneByte, ptrs[0].chartype);
+ for (unsigned int i = 0; i < strlen(hello); i++) {
+ CHECK_EQ(hello[i], ptrs[0].data[i]);
+ }
+
+ CHECK_EQ(strlen(world), ptrs[1].length);
+ CHECK_EQ(v8::String::Pointer::OneByte, ptrs[1].chartype);
+ for (unsigned int i = 0; i < strlen(world); i++) {
+ CHECK_EQ(world[i], ptrs[1].data[i]);
+ }
+ }
+ i::CompilationCache::Clear();
+ i::Heap::CollectAllGarbage(false);
+ i::Heap::CollectAllGarbage(false);
+}
+
+
+THREADED_TEST(StringPointersCons2) {
+ {
+ v8::HandleScope scope;
+ LocalContext env;
+
+ const char* hello = "hello hello hello hello hello hello ";
+ const char* world = "world world world world world world world world ";
+
+ Local<String> s = v8_str(hello);
+ Local<String> t = v8_str(world);
+ Local<String> c = v8::String::Concat(v8::String::Concat(s,t),
+ v8::String::Concat(v8::String::Concat(s,t),
+ t));
+
+ // This isn't part of the actual test. Just want to make sure that this
+ // is actually a cons string. If the following test fails, maybe
+ // increase the size of s and t.
+ i::Handle<i::String> str = v8::Utils::OpenHandle(*c);
+ CHECK(i::StringShape(*str).IsCons());
+
+
+ String::Pointer ptrs[10];
+ int used;
+
+ int chars = c->Pointers(0, ptrs, 10, &used);
+
+ CHECK_EQ(5, used);
+ CHECK_EQ(2 * strlen(hello) + 3 * strlen(world), chars);
+
+ CHECK_EQ(strlen(hello), ptrs[0].length);
+ CHECK_EQ(v8::String::Pointer::OneByte, ptrs[0].chartype);
+ CHECK_EQ('h', ptrs[0].data[0]);
+
+ CHECK_EQ(strlen(world), ptrs[1].length);
+ CHECK_EQ(v8::String::Pointer::OneByte, ptrs[1].chartype);
+ CHECK_EQ('w', ptrs[1].data[0]);
+
+ CHECK_EQ(strlen(hello), ptrs[2].length);
+ CHECK_EQ(v8::String::Pointer::OneByte, ptrs[2].chartype);
+ CHECK_EQ('h', ptrs[2].data[0]);
+
+ CHECK_EQ(strlen(world), ptrs[3].length);
+ CHECK_EQ(v8::String::Pointer::OneByte, ptrs[3].chartype);
+ CHECK_EQ('w', ptrs[3].data[0]);
+
+ CHECK_EQ(strlen(world), ptrs[4].length);
+ CHECK_EQ(v8::String::Pointer::OneByte, ptrs[4].chartype);
+ CHECK_EQ('w', ptrs[4].data[0]);
+ }
+ i::CompilationCache::Clear();
+ i::Heap::CollectAllGarbage(false);
+ i::Heap::CollectAllGarbage(false);
+}
+
+
THREADED_TEST(GlobalProperties) {
v8::HandleScope scope;
LocalContext env;
--
1.7.2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment