v/vlib/x/encoding/asn1/field_options.v

569 lines
16 KiB
V

module asn1
// This file is for supporting configure through string options.
// so, you can tag your struct field with supported attributes defined here.
// Limit of string option length
const max_string_option_length = 255
const max_attributes_length = 5
// Configuration format for field tagging.
//
// Currently, this configurations option support following string config, ie.
//
// - `class:number`, for wrapping the element with other non-universal class, for examole: `private:100`.
// - `explicit` or `implicit` mode.
// - `inner:5` for universal class form or `inner:class,constructed,number` for extended form.
// - `optional` or `optional:present' tagging for element with OPTIONAL behaviour.
// - `has_default` tagging for element with DEFAULT behaviour.
//
// Field options attributes handling.
//
// FieldOptions is a structure to accomodate and allowing configures your complex structures
// through string or arrays of string stored in FieldOptions fields.
// For example, you can tagging your fields of some element with tagging
// like `@[context_specific:10; explicit; inner:5; optional]`.
// Its will be parsed and can be used to drive encoding or decoding of Element.
pub struct FieldOptions {
mut:
// The fields `cls`, `tagnum`, `mode` and `inner` was used
// for wrapping (and unwrapping) purposes. turn some element
// into another element configured with this options.
// In the encoding (decoding) phase, it would be checked
// if this options meet required criteria.
// Limitation applied on the wrapper fields:
// 1. Wrap into UNIVERSAL is not allowed (cls != universal)
// 2. You should provide mode for wrapping, explicit or implicit.
// 3. If cls == '', no wrapping is performed, discarding all wrapper options
cls string // should cls != 'universal'
tagnum int = -1 // Provides with wrapper tag number, as n outer tag number.
mode string // explicit or implicit, depends on definition schema.
// inner should valid inner tag format, ie, universal form in single value 'number'
// or extended form in triplet value of 'class,form,number' format.
inner string
// optional field applied to element with OPTIONAL behaviour, with or without DEFAULT value.
// Set `optional` to true when this element has OPTIONAL keyword in the definition of element.
// Usually element with OPTIONAL keyword is not presents in the encoding (decoding) data.
optional bool
present bool
// This field applied to element with DEFAULT keyword behaviour.
// Its applied into wrapping of element or optionality of the element.
// If some element has DEFAULT keyword, set this field to true and gives default element
// into `default_value` field.
has_default bool
default_value ?Element
}
// `from_string` parses string as an attribute of field options.
// Its allows string similar to `application:4; optional; has_default`
// to be treated as an field options.
// See FieldOptions in `field_options.v` for more detail.
pub fn FieldOptions.from_string(s string) !FieldOptions {
if s.len == 0 {
return FieldOptions{}
}
if s.len > max_string_option_length {
return error('string option exceed limit')
}
trimmed := s.trim_space()
// check whether this string is a valid one.
if !is_asn1_options_marker(trimmed) {
return error('You have not provides correct options marker')
}
attrs := trimmed.split(';')
opt := FieldOptions.from_attrs(attrs)!
// no need to check, from_attrs already called it internally.
// opt.check_wrapper()!
return opt
}
// filtered_attrs filters and takes only supported asn1 marker from arrays of string.
fn filtered_attrs(attrs []string) []string {
mut filtered := []string{}
for attr in attrs {
item := attr.trim_space()
if is_asn1_options_marker(item) {
filtered << item
}
}
return filtered
}
// `from_attrs` parses and validates []string into FieldOptions.
pub fn FieldOptions.from_attrs(attrs []string) !FieldOptions {
mut fo := FieldOptions{}
if attrs.len == 0 {
return fo
}
mut tag_ctr := 0 // tag marker counter
mut opt_ctr := 0 // optional marker counter
mut def_ctr := 0 // has_default marker counter
mut mod_ctr := 0 // mode marker counter
mut inn_ctr := 0 // inner counter
// take only valid supported asn1 marker
filtered := filtered_attrs(attrs)
if filtered.len > max_attributes_length {
return error('max allowed filtered.len')
}
// The item has space-trimmed
for item in filtered {
if is_tag_marker(item) {
cls, num := parse_tag_marker(item)!
tag_ctr += 1
if tag_ctr > 1 {
return error('multiples tag format defined')
}
tnum := num.int()
if tnum < 0 {
return error('bad tag number')
}
fo.cls = cls
fo.tagnum = tnum
}
if is_mode_marker(item) {
value := parse_mode_marker(item)!
mod_ctr += 1
if mod_ctr > 1 {
return error('multiples mode key defined')
}
if !valid_mode_value(value) {
return error('Bad mode values')
}
fo.mode = value
}
if is_inner_tag_marker(item) {
_, value := parse_inner_tag_marker(item)!
inn_ctr += 1
if inn_ctr > 1 {
return error('multiples inner tag format defined')
}
fo.inner = value
}
if is_optional_marker(item) {
_, value := parse_optional_marker(item)!
opt_ctr += 1
if opt_ctr > 1 {
return error('multiples optional tag')
}
// when this present, its an optional
fo.optional = true
present := if valid_optional_present_bit_marker(value) { true } else { false }
fo.present = present
}
if is_default_marker(item) {
default_marker := parse_default_marker(item)!
def_ctr += 1
if def_ctr > 1 {
return error('multiples has_default flag')
}
has_default := if valid_default_marker(default_marker) { true } else { false }
fo.has_default = has_default
}
}
// check
fo.check_wrapper()!
return fo
}
// wrapper_tag makes a wrapper Tag from FieldOptions for current element.
// The form of wrapper tag depends on tagged mode being supplied,
// if implicit, its follows the inner tag being wrapped (primitive or constructed one)
// If explicit, the wrapper tag would in non-primitive form.
fn (fo FieldOptions) wrapper_tag() !Tag {
if fo.cls == '' {
return error('You cant build wrapper tag from empty string')
}
fo.check_wrapper()!
cls := TagClass.from_string(fo.cls)!
if !valid_mode_value(fo.mode) {
return error('Invalid mode')
}
inner_tag := fo.inner_tag()!
form := inner_tag.constructed
if fo.mode == 'implicit' {
// implicit tagging allows to be applied on non-constructed element, inherited from inner.
return Tag.new(cls, form, fo.tagnum)!
}
return Tag.new(cls, true, fo.tagnum)!
}
// inner_tag gets inner Tag from FieldOptions.
fn (fo FieldOptions) inner_tag() !Tag {
// universal or extended form of inner value
st := fo.inner.trim_space()
if st == '' {
return error('invalid empty inner value')
}
if valid_inner_universal_form(st) {
val := st.int()
if val < 0 || val > max_universal_tagnumber {
return error('You cant create universal tag from invalid inner value')
}
utag := universal_tag_from_int(val)!
return utag
}
if !valid_extended_inner_form(st) {
return error('invalid extended inner value')
}
c, form, n := parse_inner_extended_form(st)!
cls := TagClass.from_string(c)!
constructed := if form == 'true' { true } else { false }
number := n.int()
inn_tag := Tag.new(cls, constructed, number)!
return inn_tag
}
// install_default tries to install and sets element el as a default value when has_default flag of FieldOptions
// has been set into true, or error if has_default is false.
// When default_value has been set with some value before this, its would return error until you force it
// by setingt force flag into true.
pub fn (mut fo FieldOptions) install_default(el Element, force bool) ! {
if fo.has_default {
if fo.default_value == none {
fo.default_value = el
return
}
// not nil
if !force {
return error('set force to overide')
}
// replace the old one, or should we check its matching tag ?
fo.default_value = el
}
return error('you can not install default value when has_default being not set')
}
// check_wrapper validates wrapper's part of fields options.
fn (fo FieldOptions) check_wrapper() ! {
// Validates wrapper part
// Its discard all check when fo.cls is empty string, its marked as non-wrapped element.
if fo.cls != '' {
if !valid_tagclass_name(fo.cls) {
return error('Get unexpected fo.cls value')
}
// provides the tag number
if fo.tagnum < 0 {
return error('Get unexpected fo.tagnum}')
}
// wraps into UNIVERSAL type is not allowed
if fo.cls == 'universal' {
return error('wraps into universal class is not allowed')
}
if !valid_mode_value(fo.mode) {
return error('Invalid zonk or uncorerct mode value')
}
// when wrapped, you should provide inner tag number value.
if fo.inner == '' {
return error('You provides incorrect inner number')
}
// 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 := fo.inner.int()
if num > max_universal_tagnumber {
return error('Inner number exceed universal limit')
}
}
}
}
// WRAPPING (UNWRAPPING) OPTIONS.
//
// parse 'application=number' format
// format: `class=number` without constructed keyword.
fn parse_tag_marker(attr string) !(string, string) {
src := attr.trim_space()
if is_tag_marker(src) {
field := src.split(':')
if field.len != 2 {
return error('bad tag marker length')
}
first := field[0].trim_space()
if !valid_tagclass_name(first) {
return error('bad tag name')
}
second := field[1].trim_space()
if !valid_string_tag_number(second) {
return error('bad tag number')
}
return first, second
}
return error('not a tag marker')
}
fn is_tag_marker(attr string) bool {
return attr.starts_with('application') || attr.starts_with('private')
|| attr.starts_with('context_specific')
}
fn valid_tagclass_name(tag string) bool {
return tag == 'application' || tag == 'private' || tag == 'context_specific'
}
// it should be represented in int or hex number
fn valid_string_tag_number(s string) bool {
return s.is_int() || s.is_hex()
}
// EXPLICIT OR IMPLICIT OPTIONS.
//
// parse 'explicit [or implicit]' format.
fn parse_mode_marker(s string) !string {
item := s.trim_space()
if is_mode_marker(item) {
if !valid_mode_value(item) {
return error('bad mode value')
}
return item
}
return error('not mode marker')
}
fn is_mode_marker(attr string) bool {
return attr.starts_with('explicit') || attr.starts_with('implicit')
}
fn valid_mode_value(s string) bool {
return s == 'explicit' || s == 'implicit'
}
// INNER TAG OPTIONS.
//
// parse inner value to be used by decoder.
// 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(src string) !(string, string) {
if is_inner_tag_marker(src) {
item := src.split(':')
if item.len != 2 {
return error('bad inner tag marker length')
}
// check for inner part
key := item[0].trim_space()
if !valid_inner_tag_key(key) {
return error('bad inner key')
}
// check for universal or extended form.
// item is comma separated value.
value := item[1].trim_space()
items := value.split(',')
if items.len == 0 {
return error('no inner value')
}
// length should 1 (universal) or 3 (extended)
if items.len != 1 && items.len != 3 {
return error('Invalid items.len')
}
// if its in universal form, should be a number
if items.len == 1 {
if !valid_inner_universal_form(value) {
return error('Get invalid universal inner value')
}
}
// extended form
if items.len == 3 {
if !is_extended_inner_cls_marker(items[0].trim_space()) {
return error('Your first ext inner is not extended cls')
}
if !valid_extended_inner_cls_marker(items[0].trim_space()) {
return error('Your first ext inner is not valid ext cls')
}
// second form should be 'true' or 'false'
if !valid_extended_inner_form_marker(items[1].trim_space()) {
return error('Your ext inner form is invalid')
}
// third item should be a number
if !valid_extended_inner_number_marker(items[2].trim_space()) {
return error('invalid ext inner number part')
}
}
return key, value
}
return error('not inner tag marker')
}
fn is_inner_tag_marker(s string) bool {
return s.starts_with('inner')
}
fn valid_inner_tag_key(s string) bool {
return s == 'inner'
}
fn valid_inner_universal_form(value string) bool {
// 'inner: number' part
return valid_string_tag_number(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
}
if !is_extended_inner_cls_marker(items[0].trim_space()) {
return false
}
if !valid_extended_inner_cls_marker(items[0].trim_space()) {
return false
}
// second form should be 'true' or 'false'
if !valid_extended_inner_form_marker(items[1].trim_space()) {
return false
}
// third item should be a number
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 in comma separated value.
items := s.split(',')
if items.len != 3 {
return error('invalid extended form length')
}
cls := items[0].trim_space()
if !is_extended_inner_cls_marker(cls) {
return error('Your first ext inner is not extended cls')
}
if !valid_extended_inner_cls_marker(cls) {
return error('Your first ext inner is not valid ext cls')
}
// second form should be 'true' or 'false'
form := items[1].trim_space()
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) {
return error('invalid ext inner number part')
}
return cls, form, third
}
// allows universal class as an inner.
fn is_extended_inner_cls_marker(attr string) bool {
return is_tag_marker(attr) || attr.starts_with('universal')
}
fn valid_extended_inner_cls_marker(attr string) bool {
return valid_tagclass_name(attr) || attr == 'universal'
}
fn valid_extended_inner_form_marker(s string) bool {
return s == 'true' || s == 'false'
}
fn valid_extended_inner_number_marker(s string) bool {
return valid_string_tag_number(s)
}
// OPTIONAL.
//
// support two form of optional marker.
// - the only optional key 'optional' marker, and
// - extended bit of presence of optional, 'optional:present'
fn parse_optional_marker(attr string) !(string, string) {
values := attr.split(':')
if values.len != 1 && values.len != 2 {
return error('Bad optional length')
}
if values.len == 1 {
key := values[0].trim_space()
if is_optional_marker(key) {
if !valid_optional_key(key) {
return error('bad optional key')
}
return key, ''
}
}
if values.len == 2 {
first := values[0].trim_space()
if !valid_optional_key(first) {
return error('bad optional key')
}
second := values[1].trim_space()
if !is_optional_present_bit_marker(second) {
return error('Non optional presence bit marker')
}
if !valid_optional_present_bit_marker(second) {
return error('Not valid optional presence bit marker')
}
return first, second
}
return error('not optional marker')
}
fn is_optional_marker(attr string) bool {
return attr.starts_with('optional')
}
fn valid_optional_key(attr string) bool {
return attr == 'optional'
}
fn is_optional_present_bit_marker(attr string) bool {
return attr.starts_with('present')
}
fn valid_optional_present_bit_marker(attr string) bool {
return attr == 'present'
}
// DEFAULT OPTIONS.
//
// parse 'has_default' marker
fn parse_default_marker(attr string) !string {
item := attr.trim_space()
if is_default_marker(item) {
if !valid_default_marker(item) {
return error('bad has_default marker')
}
return item
}
return error('not has_default marker')
}
fn is_default_marker(attr string) bool {
return attr.starts_with('has_default')
}
fn valid_default_marker(attr string) bool {
return attr == 'has_default'
}
// UTILTIY
//
// is_asn1_options_marker checks if provided string is valid supported field options string.
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)
return valid
}