From ef392525cce91fa7960eb33bb5183d4fb9a70dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Mon, 17 Apr 2023 18:14:14 +0200 Subject: [PATCH 1/3] Refactor `String` header layout reflection --- src/string.cr | 19 +++++++++++++++---- src/string/builder.cr | 7 ++++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/string.cr b/src/string.cr index 9454f6d1ea03..7a11c6a09045 100644 --- a/src/string.cr +++ b/src/string.cr @@ -146,7 +146,9 @@ class String TYPE_ID = "".crystal_type_id # :nodoc: - HEADER_SIZE = sizeof({Int32, Int32, Int32}) + # + # Holds the offset to the first character byte. + HEADER_SIZE = offsetof(String, @c) include Comparable(self) @@ -266,9 +268,18 @@ class String str = GC.realloc(str, bytesize.to_u32 + HEADER_SIZE + 1) end - str_header = str.as({Int32, Int32, Int32}*) - str_header.value = {TYPE_ID, bytesize.to_i, size.to_i} - str.as(String) + str.as(Pointer(typeof(TYPE_ID))).value = TYPE_ID + str = str.as(String) + str.initialize_header(bytesize.to_i, size.to_i) + str + end + + # :nodoc: + # + # Initializes the header information of a `String` instance. + # The actual character content at `@c` is expected to be already filled and is + # unaffected by this method. + def initialize_header(@bytesize : Int32, @length : Int32 = 0) end # Builds a `String` by creating a `String::Builder` with the given initial capacity, yielding diff --git a/src/string/builder.cr b/src/string/builder.cr index d1add8dc9be2..89e8ffbd3055 100644 --- a/src/string/builder.cr +++ b/src/string/builder.cr @@ -117,9 +117,10 @@ class String::Builder < IO resize_to_capacity(real_bytesize) end - header = @buffer.as({Int32, Int32, Int32}*) - header.value = {String::TYPE_ID, @bytesize - 1, 0} - @buffer.as(String) + @buffer.as(Pointer(typeof(String::TYPE_ID))).value = String::TYPE_ID + str = @buffer.as(String) + str.initialize_header((bytesize - 1).to_i) + str end private def real_bytesize From 9199b60c0ccd3cf5f6db18183303b75ea3295e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Mon, 17 Apr 2023 21:44:00 +0200 Subject: [PATCH 2/3] Remove unnecesary and harmful bounds check --- src/string/builder.cr | 1 - 1 file changed, 1 deletion(-) diff --git a/src/string/builder.cr b/src/string/builder.cr index 89e8ffbd3055..b34cdf97d923 100644 --- a/src/string/builder.cr +++ b/src/string/builder.cr @@ -14,7 +14,6 @@ class String::Builder < IO # Make sure to also be able to hold # the header size plus the trailing zero byte capacity += String::HEADER_SIZE + 1 - String.check_capacity_in_bounds(capacity) @buffer = GC.malloc_atomic(capacity.to_u32).as(UInt8*) @bytesize = 0 From 5789771b1ba3c3227419e3abb4da6cfdfabf0ea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Tue, 18 Apr 2023 12:27:41 +0200 Subject: [PATCH 3/3] Use `set_crystal_type_id` and remove `String::TYPE_ID` --- src/string.cr | 5 +---- src/string/builder.cr | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/string.cr b/src/string.cr index 7a11c6a09045..61237013800f 100644 --- a/src/string.cr +++ b/src/string.cr @@ -142,9 +142,6 @@ require "c/string" # behavior on invalid strings. If this is undesired, `#scrub` could be used to # remove the offending byte sequences first. class String - # :nodoc: - TYPE_ID = "".crystal_type_id - # :nodoc: # # Holds the offset to the first character byte. @@ -268,7 +265,7 @@ class String str = GC.realloc(str, bytesize.to_u32 + HEADER_SIZE + 1) end - str.as(Pointer(typeof(TYPE_ID))).value = TYPE_ID + set_crystal_type_id(str) str = str.as(String) str.initialize_header(bytesize.to_i, size.to_i) str diff --git a/src/string/builder.cr b/src/string/builder.cr index b34cdf97d923..0fb77f4ca41b 100644 --- a/src/string/builder.cr +++ b/src/string/builder.cr @@ -116,7 +116,7 @@ class String::Builder < IO resize_to_capacity(real_bytesize) end - @buffer.as(Pointer(typeof(String::TYPE_ID))).value = String::TYPE_ID + String.set_crystal_type_id(@buffer) str = @buffer.as(String) str.initialize_header((bytesize - 1).to_i) str