// // Copyright (c) 2021, Brian Frank and Andy Frank // Licensed under the Academic Free License version 3.0 // // History: // 05 Aug 2021 Matthew Giannini Creation // using math ** ** A tagged ASN.1 value ** const class AsnObj { ////////////////////////////////////////////////////////////////////////// // Constructor ////////////////////////////////////////////////////////////////////////// protected new make(AsnTag[] tags, Obj? val) { idx := tags.findIndex |tag| { tag.cls.isUniv } if (idx == -1) throw ArgErr("No UNIVERSAL tag specified") if (idx != tags.size - 1) throw ArgErr("UNIVERSAL tag must be last: ${idx} != ${tags.size-1}") this.tags = tags this.val = val } ** The tags for this object. const AsnTag[] tags ** The value for this object. const Obj? val ////////////////////////////////////////////////////////////////////////// // Value ////////////////////////////////////////////////////////////////////////// ** Get the value as a `sys::Bool` Bool bool() { val } ** Is this object's universal tag a 'Boolean' Bool isBool() { univTag == AsnTag.univBool } ** Get the value as an `sys::Int`. If the value is a `math::BigInt` you may lose ** both precision and sign. Use `bigInt` to get the value explicitly ** as a `math::BigInt`. Int int() { if (val is BigInt) return ((BigInt)val).toInt return val } ** Is this object's universal tag an 'Integer' Bool isInt() { univTag == AsnTag.univInt } ** Get the value as a `math::BigInt`. BigInt bigInt() { if (val is Int) return BigInt.makeInt(val) return val } ** Get any of the binary values as a `sys::Buf`. The Buf will be a safe copy ** that can be modified. Throws `AsnErr` if the value is not a binary value. virtual Buf buf() { throw AsnErr("Not a binary type: ${this.typeof}") } ** Is this object's universal tag an 'Octet String' Bool isOcts() { univTag == AsnTag.univOcts } ** Is this an ASN.1 'Null' value Bool isNull() { val == null && univTag == AsnTag.univNull } ** Get this object as an `AsnOid` AsnOid oid() { this } ** Is this object's universal tag an 'Object Identifier' Bool isOid() { univTag == AsnTag.univOid } ** Get the value as a `sys::Str` Str str() { val } ** Get the value as a `sys::DateTime` timestamp DateTime ts() { val } ** Get this object as an `AsnColl` AsnColl coll() { this } ** Get this object as an `AsnSeq` AsnSeq seq() { this } @NoDoc virtual Bool isAny() { false } ////////////////////////////////////////////////////////////////////////// // Tagging ////////////////////////////////////////////////////////////////////////// ** Push a tag to the front of the tag chain for this value. Returns ** a new instance of this object with the current value. ** ** AsnObj.int(123).tag(AsnTag.implicit(TagClass.context, 0)) ** => [0] IMPLICIT [UNIVERSAL 2] ** AsnObj.int(123).tag(AsnTag.explicit(TagClass.app, 1)) ** => [APPLICATION 1] EXPLICIT [UNIVERSAL 2] virtual AsnObj push(AsnTag tag) { this.typeof.method("make").call([tag].addAll(this.tags), this.val) } ** Apply rules for 'EXPLICIT' and 'IMPLICIT' tags to obtain ** the set of effective tags for encoding this object. AsnTag[] effectiveTags() { acc := AsnTag[,] AsnTag? prev := null tags.each |tag| { if (prev == null) acc.add(tag) else if (prev.mode === AsnTagMode.explicit) acc.add(tag) prev = tag } return acc } ** Get the single effective tag for this object. Throws an error ** if there are multiple effective tags AsnTag tag() { etags := this.effectiveTags if (etags.size > 1) throw AsnErr("Multiple effective tags: $etags") return etags.first } ** Get the univ tag for this object AsnTag univTag() { tags.last } ** Is this a primitive type? Bool isPrimitive() { switch (univTag) { case AsnTag.univSeq: case AsnTag.univSet: return false default: return true } } ////////////////////////////////////////////////////////////////////////// // Obj ////////////////////////////////////////////////////////////////////////// final override Int hash() { res := 31 + tags.hash res = (res * 31) + valHash return res } protected virtual Int valHash() { val?.hash ?: 0} final override Bool equals(Obj? obj) { if (this === obj) return true that := obj as AsnObj if (that == null) return false // for objects, tag equality is strict these := this.tags those := that.tags if (these.size != those.size) return false eq := these.all |t,i| { t.strictEquals(those[i]) } if (!eq) return false return valEquals(that) } protected virtual Bool valEquals(AsnObj that) { this.val == that.val } override Str toStr() { "${tags} ${valStr}" } protected virtual Str valStr() { "${val}" } }