diff --git a/ext/liquid_c/block.c b/ext/liquid_c/block.c index 9ce95888..5eab0835 100644 --- a/ext/liquid_c/block.c +++ b/ext/liquid_c/block.c @@ -515,6 +515,16 @@ static VALUE block_body_disassemble(VALUE self) document_body_get_constants_ptr(entry), (const VALUE *)body->tags.data); } +static VALUE block_body_dump(VALUE self) +{ + block_body_t *body; + BlockBody_Get_Struct(self, body); + ensure_body_compiled(body); + + return document_body_dump(body->as.compiled.document_body_entry.body, + (uint32_t)body->as.compiled.document_body_entry.buffer_offset); +} + static VALUE block_body_add_evaluate_expression(VALUE self, VALUE expression) { @@ -604,6 +614,7 @@ void liquid_define_block_body() rb_define_method(cLiquidCBlockBody, "blank?", block_body_blank_p, 0); rb_define_method(cLiquidCBlockBody, "nodelist", block_body_nodelist, 0); rb_define_method(cLiquidCBlockBody, "disassemble", block_body_disassemble, 0); + rb_define_method(cLiquidCBlockBody, "dump", block_body_dump, 0); rb_define_method(cLiquidCBlockBody, "add_evaluate_expression", block_body_add_evaluate_expression, 1); rb_define_method(cLiquidCBlockBody, "add_find_variable", block_body_add_find_variable, 1); diff --git a/ext/liquid_c/document_body.c b/ext/liquid_c/document_body.c index 922920bc..74648160 100644 --- a/ext/liquid_c/document_body.c +++ b/ext/liquid_c/document_body.c @@ -132,6 +132,35 @@ void document_body_write_block_body(VALUE self, bool blank, uint32_t render_scor } } + +VALUE document_body_dump(document_body_t *body, uint32_t entrypoint_block_offset) +{ + assert(BUILTIN_TYPE(body->constants) == T_ARRAY); + + uint32_t buffer_len = (uint32_t)c_buffer_size(&body->buffer); + + VALUE constants = rb_marshal_dump(body->constants, Qnil); + uint32_t constants_len = (uint32_t)RSTRING_LEN(constants); + + VALUE str = rb_str_buf_new(sizeof(document_body_header_t) + buffer_len + constants_len); + + document_body_header_t header = { + .version = DOCUMENT_BODY_CURRENT_VERSION, + .entrypoint_block_offset = entrypoint_block_offset, + .buffer_offset = sizeof(document_body_header_t), + .buffer_len = buffer_len, + .constants_offset = sizeof(document_body_header_t) + buffer_len, + .constants_len = constants_len + }; + + rb_str_cat(str, (const char *)&header, sizeof(document_body_header_t)); + rb_str_cat(str, (const char *)body->buffer.data, buffer_len); + rb_str_append(str, constants); + + return str; +} + + void liquid_define_document_body() { cLiquidCDocumentBody = rb_define_class_under(mLiquidC, "DocumentBody", rb_cObject); diff --git a/ext/liquid_c/document_body.h b/ext/liquid_c/document_body.h index c4923cc2..7dcf43ed 100644 --- a/ext/liquid_c/document_body.h +++ b/ext/liquid_c/document_body.h @@ -26,6 +26,19 @@ typedef struct document_body { c_buffer_t buffer; } document_body_t; +/* Bump this version every time a backwards incompatible change has been made in the serialization headers. + */ +#define DOCUMENT_BODY_CURRENT_VERSION 0 + +typedef struct document_body_header { + uint32_t version; + uint32_t entrypoint_block_offset; + uint32_t buffer_offset; + uint32_t buffer_len; + uint32_t constants_offset; + uint32_t constants_len; +} document_body_header_t; + typedef struct document_body_entry { document_body_t *body; size_t buffer_offset; @@ -34,6 +47,7 @@ typedef struct document_body_entry { void liquid_define_document_body(); VALUE document_body_new_instance(); void document_body_write_block_body(VALUE self, bool blank, uint32_t render_score, vm_assembler_t *code, document_body_entry_t *entry); +VALUE document_body_dump(document_body_t *body, uint32_t entrypoint_block_offset); static inline void document_body_entry_mark(document_body_entry_t *entry) { diff --git a/ext/liquid_c/extconf.rb b/ext/liquid_c/extconf.rb index 6d823c9b..b860a7bc 100644 --- a/ext/liquid_c/extconf.rb +++ b/ext/liquid_c/extconf.rb @@ -1,5 +1,11 @@ # frozen_string_literal: true require 'mkmf' + +# if system endianness == network endianness (big endian) +if [1].pack('I') == [1].pack('N') + raise 'System incompatible with liquid-c, only little endian systems supported' +end + $CFLAGS << ' -std=c11 -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers' append_cflags('-fvisibility=hidden') # In Ruby 2.6 and earlier, the Ruby headers did not have struct timespec defined diff --git a/lib/liquid/c.rb b/lib/liquid/c.rb index d315612e..0f670732 100644 --- a/lib/liquid/c.rb +++ b/lib/liquid/c.rb @@ -109,6 +109,18 @@ def parse(tokenizer, parse_context) end end +Liquid::Template.class_eval do + def dump + @root.dump + end +end + +Liquid::Document.class_eval do + def dump + @body.dump + end +end + Liquid::Variable.class_eval do class << self # @api private