From 2f53e2d219cb0baac31887523366750bfd3c1942 Mon Sep 17 00:00:00 2001 From: blackshirt Date: Sun, 24 Nov 2024 00:00:46 +0700 Subject: [PATCH] x.encoding.asn1: improve performance (#22948) --- vlib/x/encoding/asn1/bench/bench.v | 6 +-- vlib/x/encoding/asn1/common.v | 8 +--- vlib/x/encoding/asn1/element_decode.v | 19 +++++---- vlib/x/encoding/asn1/element_encode.v | 11 ++--- vlib/x/encoding/asn1/field_options.v | 60 ++++++++++----------------- 5 files changed, 43 insertions(+), 61 deletions(-) diff --git a/vlib/x/encoding/asn1/bench/bench.v b/vlib/x/encoding/asn1/bench/bench.v index 915513492..e3bb8a5a1 100644 --- a/vlib/x/encoding/asn1/bench/bench.v +++ b/vlib/x/encoding/asn1/bench/bench.v @@ -9,7 +9,6 @@ import x.encoding.asn1 // type [1] EXPLICIT OBJECT IDENTIFIER // } // ``` - struct Example { greeting asn1.Utf8String answer asn1.Integer @@ -28,6 +27,7 @@ fn (ex Example) tag() asn1.Tag { fn (ex Example) payload() ![]u8 { kd := asn1.KeyDefault(map[string]asn1.Element{}) payload := asn1.make_payload[Example](ex, kd)! + return payload } @@ -69,7 +69,7 @@ fn main() { ex := Example{ greeting: asn1.Utf8String.new('Hello')! answer: asn1.Integer.from_int(42) - tipe: asn1.ObjectIdentifier.new('1.3.6.1.3')! + tipe: asn1.ObjectIdentifier.from_ints([1, 3, 6, 1, 3])! } iterations := 1000 @@ -84,7 +84,7 @@ fn main() { avg_enc_time := total_enc_time / iterations println('Average example encode time: ${avg_enc_time} µs') - println('Benchmarking ASN.1 decode (with asn.decode)...') + println('Benchmarking ASN.1 decode (with asn1.decode)...') mut total_dec_time := i64(0) for _ in 0 .. iterations { sw := time.new_stopwatch() diff --git a/vlib/x/encoding/asn1/common.v b/vlib/x/encoding/asn1/common.v index 29a4a158c..fa0edd4e4 100644 --- a/vlib/x/encoding/asn1/common.v +++ b/vlib/x/encoding/asn1/common.v @@ -33,9 +33,6 @@ fn parse_universal(tag Tag, content []u8) !Element { } fn parse_universal_primitive(tag Tag, content []u8) !Element { - if tag.class != .universal { - return error('parse on non-universal type') - } if tag.constructed { return error('parse on constructed type') } @@ -93,10 +90,7 @@ fn parse_universal_primitive(tag Tag, content []u8) !Element { } fn parse_universal_constructed(tag Tag, content []u8) !Element { - if tag.tag_class() != .universal { - return error('parse on non-universal class') - } - if !tag.is_constructed() { + if !tag.constructed { return error('parse on non-constructed type') } match tag.tag_number() { diff --git a/vlib/x/encoding/asn1/element_decode.v b/vlib/x/encoding/asn1/element_decode.v index 82afa218d..6602445e5 100644 --- a/vlib/x/encoding/asn1/element_decode.v +++ b/vlib/x/encoding/asn1/element_decode.v @@ -19,8 +19,13 @@ module asn1 // decoded_obj := asn1.decode(bytes_data)! // assert decoded_obj.equal(original_obj) // ``` -pub fn decode(src []u8) !Element { - return decode_with_options(src, '') +pub fn decode(bytes []u8) !Element { + // call Element.decode_with_rule directly + el, pos := Element.decode_with_rule(bytes, 0, .der)! + if pos > bytes.len { + return error('decode on data with trailing data') + } + return el } // decode_with_options decodes single element from bytes with options support, its not allowing trailing data. @@ -49,8 +54,9 @@ pub fn decode(src []u8) !Element { // dump(obj_3) // output: obj_3: asn1.Element(Utf8String: (hi)) // ``` pub fn decode_with_options(bytes []u8, opt string) !Element { + // if null-option length, call Element.decode_with_rule directly if opt.len == 0 { - el, pos := Element.decode(bytes)! + el, pos := Element.decode_with_rule(bytes, 0, .der)! if pos > bytes.len { return error('decode on data with trailing data') } @@ -136,7 +142,7 @@ pub fn (el Element) unwrap_with_field_options(fo FieldOptions) !Element { if el.tag().class == .universal { return error('you cant unwrap universal element') } - // its also happens to fo.cls, should not universal class + // its also happens to fo.cls, should not be an universal class if fo.cls == 'universal' { return error('you cant unwrap universal element') } @@ -170,9 +176,8 @@ pub fn (el Element) unwrap_with_field_options(fo FieldOptions) !Element { return error('Element tag unequal with tag from options') } - inner_el := unwrap(el, mode, inner_tag)! - - return inner_el + // return unwrapped element + return unwrap(el, mode, inner_tag)! } // unwrap the provided element, turn into inner element. diff --git a/vlib/x/encoding/asn1/element_encode.v b/vlib/x/encoding/asn1/element_encode.v index fcfa2a016..7ce507dfc 100644 --- a/vlib/x/encoding/asn1/element_encode.v +++ b/vlib/x/encoding/asn1/element_encode.v @@ -44,18 +44,16 @@ pub fn encode_with_options(el Element, opt string) ![]u8 { // `encode_with_field_options` serializes this element into bytes array with options defined in fo. pub fn encode_with_field_options(el Element, fo FieldOptions) ![]u8 { - return el.encode_with_field_options(fo) + return el.encode_with_field_options(fo)! } fn (el Element) encode_with_options(opt string) ![]u8 { // treated as without option when nil if opt.len == 0 { - out := encode_with_rule(el, .der)! - return out + return encode_with_rule(el, .der)! } fo := FieldOptions.from_string(opt)! - out := el.encode_with_field_options(fo)! - return out + return el.encode_with_field_options(fo)! } // encode_with_field_options serializes element into bytes arrays with supplied FieldOptions. @@ -121,8 +119,7 @@ fn (el Element) into_optional(with_present bool) !Element { if el is Optional { return error('already optional element') } - opt := Optional.new(el, with_present)! - return opt + return Optional.new(el, with_present)! } // apply_field_options applies rules in field options into current element diff --git a/vlib/x/encoding/asn1/field_options.v b/vlib/x/encoding/asn1/field_options.v index 7df55becc..4fbfde500 100644 --- a/vlib/x/encoding/asn1/field_options.v +++ b/vlib/x/encoding/asn1/field_options.v @@ -76,8 +76,8 @@ pub fn FieldOptions.from_string(s string) !FieldOptions { } attrs := trimmed.split(';') opt := FieldOptions.from_attrs(attrs)! - // checks - opt.check_wrapper()! + // no need to check, from_attrs already called it internally. + // opt.check_wrapper()! return opt } @@ -113,8 +113,8 @@ pub fn FieldOptions.from_attrs(attrs []string) !FieldOptions { return error('max allowed filtered.len') } - for attr in filtered { - item := attr.trim_space() + // The item has space-trimmed + for item in filtered { if !is_tag_marker(item) && !is_optional_marker(item) && !is_default_marker(item) && !is_mode_marker(item) && !is_inner_tag_marker(item) { return error('unsupported keyword') @@ -274,13 +274,13 @@ fn (fo FieldOptions) check_wrapper() ! { if fo.inner == '' { return error('You provides incorrect inner number') } - inner := fo.inner.trim_space() - if !valid_inner_universal_form(inner) && !valid_extended_inner_form(inner) { + // inner := fo.inner.trim_space() + if !valid_inner_universal_form(fo.inner) && !valid_extended_inner_form(fo.inner) { return error('invalid inner value format') } if valid_inner_universal_form(fo.inner) { - val := fo.inner.trim_space() - num := val.int() + // val := fo.inner.trim_space() + num := fo.inner.int() if num > max_universal_tagnumber { return error('Inner number exceed universal limit') } @@ -355,8 +355,7 @@ fn valid_mode_value(s string) bool { // support two format: // - unviersal inner format in the form `inner:number`, where number is universal class number. // - extended inner format in the form 'inner:class,form,number' for more broad support of the inner class. -fn parse_inner_tag_marker(attr string) !(string, string) { - src := attr.trim_space() +fn parse_inner_tag_marker(src string) !(string, string) { if is_inner_tag_marker(src) { item := src.split(':') if item.len != 2 { @@ -414,46 +413,37 @@ fn valid_inner_tag_key(s string) bool { return s == 'inner' } -fn valid_inner_universal_form(s string) bool { +fn valid_inner_universal_form(value string) bool { // 'inner: number' part - value := s.trim_space() return valid_string_tag_number(value) } -fn valid_extended_inner_form(s string) bool { - // 'inner:class,form,number' part - value := s.trim_space() - // comma separated value. +fn valid_extended_inner_form(value string) bool { + // 'inner:class,form,number' part in comma separated value. items := value.split(',') if items.len != 3 { return false } - cls := items[0].trim_space() - if !is_extended_inner_cls_marker(cls) { + if !is_extended_inner_cls_marker(items[0].trim_space()) { return false } - if !valid_extended_inner_cls_marker(cls) { + if !valid_extended_inner_cls_marker(items[0].trim_space()) { return false } // second form should be 'true' or 'false' - form := items[1].trim_space() - if !valid_extended_inner_form_marker(form) { + if !valid_extended_inner_form_marker(items[1].trim_space()) { return false } - // third item should be a number - third := items[2].trim_space() - if !valid_extended_inner_number_marker(third) { + if !valid_extended_inner_number_marker(items[2].trim_space()) { return false } return true } fn parse_inner_extended_form(s string) !(string, string, string) { - // 'inner:class,form,number' part - value := s.trim_space() - // comma separated value. - items := value.split(',') + // 'inner:class,form,number' part in comma separated value. + items := s.split(',') if items.len != 3 { return error('invalid extended form length') } @@ -469,7 +459,6 @@ fn parse_inner_extended_form(s string) !(string, string, string) { if !valid_extended_inner_form_marker(form) { return error('Your ext inner form is invalid') } - // third item should be a number third := items[2].trim_space() if !valid_extended_inner_number_marker(third) { @@ -487,13 +476,11 @@ fn valid_extended_inner_cls_marker(attr string) bool { return valid_tagclass_name(attr) || attr == 'universal' } -fn valid_extended_inner_form_marker(attr string) bool { - s := attr.trim_space() +fn valid_extended_inner_form_marker(s string) bool { return s == 'true' || s == 'false' } -fn valid_extended_inner_number_marker(attr string) bool { - s := attr.trim_space() +fn valid_extended_inner_number_marker(s string) bool { return valid_string_tag_number(s) } @@ -503,8 +490,7 @@ fn valid_extended_inner_number_marker(attr string) bool { // - the only optional key 'optional' marker, and // - extended bit of presence of optional, 'optional:present' fn parse_optional_marker(attr string) !(string, string) { - opt := attr.trim_space() - values := opt.split(':') + values := attr.split(':') if values.len != 1 && values.len != 2 { return error('Bad optional length') } @@ -576,8 +562,8 @@ fn valid_default_marker(attr string) bool { // UTILTIY // // is_asn1_options_marker checks if provided string is valid supported field options string. -fn is_asn1_options_marker(s string) bool { - item := s.trim_space() +fn is_asn1_options_marker(item string) bool { + // item := s.trim_space() // belowng to one of five supported marker. valid := is_tag_marker(item) || is_mode_marker(item) || is_inner_tag_marker(item) || is_optional_marker(item) || is_default_marker(item) -- 2.39.5