From 94905820e6da47fbb60eb4f30a3c553ba6d73a95 Mon Sep 17 00:00:00 2001 From: blackshirt Date: Mon, 25 Nov 2024 16:08:54 +0700 Subject: [PATCH] x.encoding.asn1: cleanup code, improve performance (#22968) --- vlib/x/encoding/asn1/element.v | 6 +- vlib/x/encoding/asn1/element_decode.v | 3 +- vlib/x/encoding/asn1/element_encode.v | 69 ++++++++++------------- vlib/x/encoding/asn1/field_options.v | 4 -- vlib/x/encoding/asn1/other_element.v | 81 +++++---------------------- vlib/x/encoding/asn1/sequence.v | 14 ----- vlib/x/encoding/asn1/set.v | 14 ----- vlib/x/encoding/asn1/tagged_test.v | 4 +- vlib/x/encoding/asn1/time.v | 5 +- vlib/x/encoding/asn1/time_test.v | 2 + 10 files changed, 54 insertions(+), 148 deletions(-) diff --git a/vlib/x/encoding/asn1/element.v b/vlib/x/encoding/asn1/element.v index 4a39ec1741..3200b069a7 100644 --- a/vlib/x/encoding/asn1/element.v +++ b/vlib/x/encoding/asn1/element.v @@ -67,8 +67,7 @@ pub fn (el Element) into_object[T]() !T { // length tells the payload length of this element. pub fn (el Element) length() !int { - payload := el.payload()! - return payload.len + return el.payload()!.len } // UTILITY HELPER FOR ELEMENT @@ -225,8 +224,7 @@ fn (el Element) equal_payload(other Element) bool { } fn Element.decode(src []u8) !(Element, int) { - el, pos := Element.decode_with_rule(src, 0, .der)! - return el, pos + return Element.decode_with_rule(src, 0, .der)! } // decode deserializes back bytes in src from offet `loc` into Element. diff --git a/vlib/x/encoding/asn1/element_decode.v b/vlib/x/encoding/asn1/element_decode.v index 6602445e51..b19b2e24bc 100644 --- a/vlib/x/encoding/asn1/element_decode.v +++ b/vlib/x/encoding/asn1/element_decode.v @@ -99,8 +99,7 @@ pub fn decode_with_field_options(bytes []u8, fo FieldOptions) !Element { return error('Get different tag number') } // TODO: default - el := tlv.unwrap_with_field_options(fo)! - return el + return tlv.unwrap_with_field_options(fo)! } fn decode_optional(bytes []u8, expected_tag Tag) !Element { diff --git a/vlib/x/encoding/asn1/element_encode.v b/vlib/x/encoding/asn1/element_encode.v index 7ce507dfc9..136a5e017c 100644 --- a/vlib/x/encoding/asn1/element_encode.v +++ b/vlib/x/encoding/asn1/element_encode.v @@ -39,25 +39,16 @@ pub fn encode(el Element) ![]u8 { // assert explicit_out == [u8(0xA5), 0x04, 0x0C, 0x02, 0x68, 0x69] // ``` pub fn encode_with_options(el Element, opt string) ![]u8 { - return el.encode_with_options(opt)! -} - -// `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)! -} - -fn (el Element) encode_with_options(opt string) ![]u8 { - // treated as without option when nil + // treated as without option when empty if opt.len == 0 { return encode_with_rule(el, .der)! } fo := FieldOptions.from_string(opt)! - return el.encode_with_field_options(fo)! + return encode_with_field_options(el, fo)! } -// encode_with_field_options serializes element into bytes arrays with supplied FieldOptions. -fn (el Element) encode_with_field_options(fo FieldOptions) ![]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 { // validates options again this element. el.validate_options(fo)! @@ -81,33 +72,7 @@ fn (el Element) encode_with_field_options(fo FieldOptions) ![]u8 { return new_el.encode()! } // otherwise, just serializing it - out := encode_with_rule(new_el, .der)! - - return out -} - -// encode_with_rule encodes element into bytes array with rule. -fn encode_with_rule(el Element, rule EncodingRule) ![]u8 { - if rule != .der && rule != .ber { - return error('Element: unsupported rule') - } - mut dst := []u8{} - - // when this element is Optional without presence flag, by default would - // serialize this element into empty bytes otherwise, would serialize underlying element. - if el is Optional { - return el.encode()! - } - // otherwise, just serializes as normal - el.tag().encode_with_rule(mut dst, rule)! - // calculates the length of element, and serialize this length - payload := el.payload()! - length := Length.new(payload.len)! - length.encode_with_rule(mut dst, rule)! - // append the element payload to destination - dst << payload - - return dst + return encode_with_rule(new_el, .der)! } // Helper for wrapping element @@ -201,3 +166,27 @@ fn wrap(el Element, cls TagClass, number int, mode TaggedMode) !Element { } } } + +// encode_with_rule encodes element into bytes array with rule. +fn encode_with_rule(el Element, rule EncodingRule) ![]u8 { + if rule != .der && rule != .ber { + return error('Element: unsupported rule') + } + mut dst := []u8{} + + // when this element is Optional without presence flag, by default would + // serialize this element into empty bytes otherwise, would serialize underlying element. + if el is Optional { + return el.encode()! + } + // otherwise, just serializes as normal + el.tag().encode_with_rule(mut dst, rule)! + // calculates the length of element, and serialize this length + payload := el.payload()! + length := Length.new(payload.len)! + length.encode_with_rule(mut dst, rule)! + // append the element payload to destination + dst << payload + + return dst +} diff --git a/vlib/x/encoding/asn1/field_options.v b/vlib/x/encoding/asn1/field_options.v index 4fbfde500a..4ced8f3d70 100644 --- a/vlib/x/encoding/asn1/field_options.v +++ b/vlib/x/encoding/asn1/field_options.v @@ -115,10 +115,6 @@ pub fn FieldOptions.from_attrs(attrs []string) !FieldOptions { // 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') - } if is_tag_marker(item) { cls, num := parse_tag_marker(item)! tag_ctr += 1 diff --git a/vlib/x/encoding/asn1/other_element.v b/vlib/x/encoding/asn1/other_element.v index 267d213dab..232b6cada8 100644 --- a/vlib/x/encoding/asn1/other_element.v +++ b/vlib/x/encoding/asn1/other_element.v @@ -46,19 +46,6 @@ pub fn RawElement.new(tag Tag, content []u8) !RawElement { return RawElement{ tag: tag content: content - // Issues: without set this to `none`, when compiled with `-cstrict` options, its would bring - // into failed compiles error: - // ================= C compilation error (from clang): ============== - // error: incompatible pointer types passing '_option_x__encoding__asn1__Tag *' - // (aka 'struct _option_x__encoding__asn1__Tag *') to parameter of type '_option *' - // (aka 'struct _option *') [-Werror,-Wincompatible-pointer-types] - // cc: _option_none(&(x__encoding__asn1__Tag[]) - // { ((x__encoding__asn1__Tag){.__v_class = - // x__encoding__asn1__TagClass__universal,.constructed = 0,.number = 0,})}, - // &_t6, sizeof(x__encoding__asn1__Tag)); ``` - inner_tag: none - mode: none - default_value: none } } @@ -93,19 +80,9 @@ pub fn (r RawElement) payload() ![]u8 { return r.content } -// force_set_mode forces to change tagged mode of RawElement into mode. -// It will change how the element was interpreted. -pub fn (mut r RawElement) force_set_mode(mode TaggedMode) ! { - r.set_mode_with_flag(mode, true)! -} - -// set_mode sets the tagged mode of the RawElement into mode. -// If you want force it to use the mode, use `force_set_mode`. -pub fn (mut r RawElement) set_mode(mode TaggedMode) ! { - r.set_mode_with_flag(mode, false)! -} - -fn (mut r RawElement) set_mode_with_flag(mode TaggedMode, force bool) ! { +// set_mode sets the RawElement tagged mode, in explicit or implicit mode. If the mode has been set, +// it would drop into error until you forces it by setting force flag into true value, ie, replaces the old one. +pub fn (mut r RawElement) set_mode(mode TaggedMode, force bool) ! { if r.tag.class == .universal { return error('No need it on universal class') } @@ -120,28 +97,15 @@ fn (mut r RawElement) set_mode_with_flag(mode TaggedMode, force bool) ! { r.mode = mode } -// set_inner_tag sets the inner tag of the RawElement into inner_tag. -// If its already set, it would return error. -// Use `force_set_inner_tag` instead to force it. -pub fn (mut r RawElement) set_inner_tag(inner_tag Tag) ! { - r.set_inner_tag_with_flag(inner_tag, false)! -} - -// force_set_inner_tag forces to set the inner tag of the RawElement into inner_tag -// even its has been set previously. -pub fn (mut r RawElement) force_set_inner_tag(inner_tag Tag) ! { - r.set_inner_tag_with_flag(inner_tag, true)! -} - -fn (mut r RawElement) set_inner_tag_with_flag(inner_tag Tag, force bool) ! { +// set_inner_tag sets the inner tag of the RawElement into inner_tag value. If it has been already set, +// it would be an error until you setting force flag into true value to replace the old one. +pub fn (mut r RawElement) set_inner_tag(inner_tag Tag, force bool) ! { // not needed in universal class if r.tag.class == .universal { return error('No need it on universal class') } - // we need mode first - mode := r.mode or { return error('unmeet_requirement, set the mode first') } - - // when its explicit, compares the provided tag with tag from the inner element. + mode := r.mode or { return error('You dont set any mode') } + // when its in explicit mode, compares the provided tag with tag from the inner element. if mode == .explicit { if !r.tag.constructed { return error('unmeet_requirement, explicit should be constructed') @@ -164,17 +128,8 @@ fn (mut r RawElement) set_inner_tag_with_flag(inner_tag Tag, force bool) ! { } // force_set_default_value forces set default value of this RawElement into value. -pub fn (mut r RawElement) force_set_default_value(value Element) ! { - r.set_default_value_with_flag(value, true)! -} - -// set_default_value sets the default value of this RawElement to some value. -pub fn (mut r RawElement) set_default_value(value Element) ! { - r.set_default_value_with_flag(value, false)! -} - -fn (mut r RawElement) set_default_value_with_flag(value Element, force bool) ! { - // default value of this element should have equal tag. +fn (mut r RawElement) set_default_value(value Element, force bool) ! { + // default value of this element should have an equal tag. if !value.tag().equal(r.tag) { return error('You provides unequal tag for default value') } @@ -189,10 +144,8 @@ fn (mut r RawElement) set_default_value_with_flag(value Element, force bool) ! { } // inner_tag returns the inner tag of the RawElement if it exists, or error on fails. -pub fn (r RawElement) inner_tag() !Tag { - inner_tag := r.inner_tag or { return error(' r.inner_tag is not set') } - - return inner_tag +pub fn (r RawElement) inner_tag() ?Tag { + return r.inner_tag } // inner_element returns the inner element of the RawElement if its exists. @@ -200,9 +153,8 @@ pub fn (r RawElement) inner_element() !Element { if r.tag.class == .universal { return error('inner element from universal class is not availables') } - mode := r.mode or { return err } inner_tag := r.inner_tag or { return err } - + mode := r.mode or { return error('You dont set any mode') } if mode == .explicit { if !r.tag.constructed { return error('tag should be constructed when in explicit') @@ -302,11 +254,8 @@ fn ContextElement.decode_raw(bytes []u8) !(ContextElement, int) { next := content_pos + length // Raw ContextElement, you should provide mode and inner tag. ctx := ContextElement{ - tag: tag - content: content - inner_tag: none - mode: none - default_value: none + tag: tag + content: content } return ctx, next } diff --git a/vlib/x/encoding/asn1/sequence.v b/vlib/x/encoding/asn1/sequence.v index 3d1aff9815..c0c31798c8 100644 --- a/vlib/x/encoding/asn1/sequence.v +++ b/vlib/x/encoding/asn1/sequence.v @@ -32,13 +32,6 @@ mut: fields []Element } -fn (s Sequence) str() string { - if s.fields.len == 0 { - return 'SEQUENCE: ' - } - return 'SEQUENCE: ${s.fields.len} elements.' -} - // new creates new Sequence with default size. pub fn Sequence.new() !Sequence { return Sequence.new_with_size(default_sequence_size)! @@ -240,13 +233,6 @@ mut: fields []T } -fn (s SequenceOf[T]) str() string { - if s.fields.len == 0 { - return 'SEQUENCE OF ()' - } - return 'SEQUENCE OF (${s.fields.len} ${typeof(s).name})' -} - // SequenceOf.new creates a new SequenceOf[T] pub fn SequenceOf.new[T]() SequenceOf[T] { return SequenceOf[T]{} diff --git a/vlib/x/encoding/asn1/set.v b/vlib/x/encoding/asn1/set.v index da421b97be..b7f4f01401 100644 --- a/vlib/x/encoding/asn1/set.v +++ b/vlib/x/encoding/asn1/set.v @@ -23,13 +23,6 @@ mut: fields []Element } -fn (s Set) str() string { - if s.fields.len == 0 { - return 'SET ()' - } - return 'SET (${s.fields.len} Elements)' -} - // creates a new Set with default size. pub fn Set.new() !Set { return Set.new_with_size(default_set_size)! @@ -247,13 +240,6 @@ pub fn SetOf.new[T]() !SetOf[T] { return SetOf[T]{} } -fn (s SetOf[T]) str() string { - if s.fields.len == 0 { - return 'SET OF ()' - } - return 'SET OF (${s.fields.len} ${typeof(s).name})' -} - // from_list creates new SetOf type T from arrays of T. pub fn SetOf.from_list[T](els []T) !SetOf[T] { $if T !is Element { diff --git a/vlib/x/encoding/asn1/tagged_test.v b/vlib/x/encoding/asn1/tagged_test.v index 12dfa442f3..c9eaad0adb 100644 --- a/vlib/x/encoding/asn1/tagged_test.v +++ b/vlib/x/encoding/asn1/tagged_test.v @@ -59,8 +59,8 @@ Example ::= SEQUENCE { assert els[1] is Integer mut els2 := els[2] as ContextElement - els2.set_mode(.explicit)! - els2.set_inner_tag(default_oid_tag)! + els2.set_mode(.explicit, true)! + els2.set_inner_tag(default_oid_tag, true)! out.clear() out = encode(els2)! diff --git a/vlib/x/encoding/asn1/time.v b/vlib/x/encoding/asn1/time.v index a728a1f39b..04e680c725 100644 --- a/vlib/x/encoding/asn1/time.v +++ b/vlib/x/encoding/asn1/time.v @@ -50,7 +50,8 @@ pub fn UtcTime.new(s string) !UtcTime { } } -fn UtcTime.from_time(t time.Time) !UtcTime { +// from_time creates a new UtcTime element from standard `time.Time` in UTC time format. +pub fn UtcTime.from_time(t time.Time) !UtcTime { // changes into utc time utime := t.local_to_utc() s := utime.custom_format(default_utctime_format) // 20241113060446+0 @@ -226,7 +227,7 @@ pub fn GeneralizedTime.new(s string) !GeneralizedTime { } } -// from_time creates GeneralizedTime element from tine.Time (as an UTC time). +// from_time creates GeneralizedTime element from standard `time.Time` (as an UTC time). pub fn GeneralizedTime.from_time(t time.Time) !GeneralizedTime { u := t.local_to_utc() s := u.custom_format(default_genztime_format) diff --git a/vlib/x/encoding/asn1/time_test.v b/vlib/x/encoding/asn1/time_test.v index b0637e8429..f3348469ed 100644 --- a/vlib/x/encoding/asn1/time_test.v +++ b/vlib/x/encoding/asn1/time_test.v @@ -116,6 +116,8 @@ fn test_create_generalizedtime_from_std_time() ! { gtc := GeneralizedTime.from_time(now)! assert gtb.value == gtc.value + assert gtb.value == '20241113174550Z' + assert gtc.value == '20241113174550Z' tt := gtc.into_utctime()! assert now == tt