diff --git a/constants.go b/constants.go index 758a8bb..3b472b2 100644 --- a/constants.go +++ b/constants.go @@ -26,3 +26,41 @@ const ( RestrictionTypeRequireLI RestrictionType = 2 RestrictionTypeUndefined RestrictionType = 3 ) + +const ( + bitsBool = 1 + bitsChar = 6 + bitsTime = 36 + + bitsSegmentType = 3 + + bitsVersion = 6 + bitsCreated = bitsTime + bitsLastUpdated = bitsTime + bitsCmpId = 12 + bitsCmpVersion = 12 + bitsConsentScreen = 6 + bitsConsentLanguage = bitsChar * 2 + bitsVendorListVersion = 12 + bitsTcfPolicyVersion = 6 + bitsIsServiceSpecific = bitsBool + bitsUseNonStandardTexts = bitsBool + bitsSpecialFeatureOptIns = 12 + bitsPurposesConsent = 24 + bitsPurposesLITransparency = 24 + bitsPurposeOneTreatment = bitsBool + bitsPublisherCC = bitsChar * 2 + + bitsMaxVendorId = 16 + bitsIsRangeEncoding = bitsBool + bitsNumEntries = 12 + bitsVendorId = 16 + + bitsNumPubRestrictions = 12 + bitsPubRestrictionsEntryPurposeId = 6 + bitsPubRestrictionsEntryRestrictionType = 2 + + bitsPubPurposesConsent = 24 + bitsPubPurposesLITransparency = 24 + bitsNumCustomPurposes = 6 +) diff --git a/decode.go b/decode.go index 25f4fbb..ac76d86 100644 --- a/decode.go +++ b/decode.go @@ -29,7 +29,7 @@ func GetVersion(s string) (version TcfVersion, err error) { } var e = newTCEncoder(b) - return TcfVersion(e.readInt(6)), nil + return TcfVersion(e.readInt(bitsVersion)), nil } // Decodes a segment value and returns the SegmentType @@ -51,7 +51,7 @@ func GetSegmentType(segment string) (segmentType SegmentType, err error) { } var e = newTCEncoder(b) - return SegmentType(e.readInt(3)), nil + return SegmentType(e.readInt(bitsSegmentType)), nil } // Decode a TC String and returns it as a TCData structure @@ -138,43 +138,40 @@ func DecodeCoreString(coreString string) (c *CoreString, err error) { var e = newTCEncoder(b) c = &CoreString{} - c.Version = e.readInt(6) + c.Version = e.readInt(bitsVersion) c.Created = e.readTime() c.LastUpdated = e.readTime() - c.CmpId = e.readInt(12) - c.CmpVersion = e.readInt(12) - c.ConsentScreen = e.readInt(6) - c.ConsentLanguage = e.readIsoCode() - c.VendorListVersion = e.readInt(12) - c.TcfPolicyVersion = e.readInt(6) + c.CmpId = e.readInt(bitsCmpId) + c.CmpVersion = e.readInt(bitsCmpVersion) + c.ConsentScreen = e.readInt(bitsConsentScreen) + c.ConsentLanguage = e.readChars(bitsConsentLanguage) + c.VendorListVersion = e.readInt(bitsVendorListVersion) + c.TcfPolicyVersion = e.readInt(bitsTcfPolicyVersion) c.IsServiceSpecific = e.readBool() c.UseNonStandardStacks = e.readBool() - c.SpecialFeatureOptIns = e.readBitField(12) - c.PurposesConsent = e.readBitField(24) - c.PurposesLITransparency = e.readBitField(24) + c.SpecialFeatureOptIns = e.readBitField(bitsSpecialFeatureOptIns) + c.PurposesConsent = e.readBitField(bitsPurposesConsent) + c.PurposesLITransparency = e.readBitField(bitsPurposesLITransparency) c.PurposeOneTreatment = e.readBool() - c.PublisherCC = e.readIsoCode() + c.PublisherCC = e.readChars(bitsPublisherCC) - c.MaxVendorId = e.readInt(16) + c.MaxVendorId = e.readInt(bitsMaxVendorId) c.IsRangeEncoding = e.readBool() if c.IsRangeEncoding { - c.NumEntries = e.readInt(12) - c.RangeEntries = e.readRangeEntries(uint(c.NumEntries)) + c.NumEntries, c.RangeEntries = e.readRangeEntries() } else { c.VendorsConsent = e.readBitField(uint(c.MaxVendorId)) } - c.MaxVendorIdLI = e.readInt(16) + c.MaxVendorIdLI = e.readInt(bitsMaxVendorId) c.IsRangeEncodingLI = e.readBool() if c.IsRangeEncodingLI { - c.NumEntriesLI = e.readInt(12) - c.RangeEntriesLI = e.readRangeEntries(uint(c.NumEntriesLI)) + c.NumEntriesLI, c.RangeEntriesLI = e.readRangeEntries() } else { c.VendorsLITransparency = e.readBitField(uint(c.MaxVendorIdLI)) } - c.NumPubRestrictions = e.readInt(12) - c.PubRestrictions = e.readPubRestrictions(uint(c.NumPubRestrictions)) + c.NumPubRestrictions, c.PubRestrictions = e.readPubRestrictions() return c, nil } @@ -195,18 +192,17 @@ func DecodeDisclosedVendors(disclosedVendors string) (d *DisclosedVendors, err e var e = newTCEncoder(b) d = &DisclosedVendors{} - d.SegmentType = e.readInt(3) - d.MaxVendorId = e.readInt(16) + d.SegmentType = e.readInt(bitsSegmentType) + d.MaxVendorId = e.readInt(bitsMaxVendorId) d.IsRangeEncoding = e.readBool() if d.IsRangeEncoding { - d.NumEntries = e.readInt(12) - d.RangeEntries = e.readRangeEntries(uint(d.NumEntries)) + d.NumEntries, d.RangeEntries = e.readRangeEntries() } else { d.DisclosedVendors = e.readBitField(uint(d.MaxVendorId)) } - if d.SegmentType != 1 { - err = fmt.Errorf("disclosed vendors segment type must be 1") + if d.SegmentType != int(SegmentTypeDisclosedVendors) { + err = fmt.Errorf("disclosed vendors segment type must be %d", SegmentTypeDisclosedVendors) return nil, err } @@ -229,18 +225,17 @@ func DecodeAllowedVendors(allowedVendors string) (a *AllowedVendors, err error) var e = newTCEncoder(b) a = &AllowedVendors{} - a.SegmentType = e.readInt(3) - a.MaxVendorId = e.readInt(16) + a.SegmentType = e.readInt(bitsSegmentType) + a.MaxVendorId = e.readInt(bitsMaxVendorId) a.IsRangeEncoding = e.readBool() if a.IsRangeEncoding { - a.NumEntries = e.readInt(12) - a.RangeEntries = e.readRangeEntries(uint(a.NumEntries)) + a.NumEntries, a.RangeEntries = e.readRangeEntries() } else { a.AllowedVendors = e.readBitField(uint(a.MaxVendorId)) } - if a.SegmentType != 2 { - return nil, fmt.Errorf("allowed vendors segment type must be 2") + if a.SegmentType != int(SegmentTypeAllowedVendors) { + return nil, fmt.Errorf("allowed vendors segment type must be %d", SegmentTypeAllowedVendors) } return a, nil @@ -262,15 +257,15 @@ func DecodePublisherTC(publisherTC string) (p *PublisherTC, err error) { var e = newTCEncoder(b) p = &PublisherTC{} - p.SegmentType = e.readInt(3) - p.PubPurposesConsent = e.readBitField(24) - p.PubPurposesLITransparency = e.readBitField(24) - p.NumCustomPurposes = e.readInt(6) + p.SegmentType = e.readInt(bitsSegmentType) + p.PubPurposesConsent = e.readBitField(bitsPubPurposesConsent) + p.PubPurposesLITransparency = e.readBitField(bitsPubPurposesLITransparency) + p.NumCustomPurposes = e.readInt(bitsNumCustomPurposes) p.CustomPurposesConsent = e.readBitField(uint(p.NumCustomPurposes)) p.CustomPurposesLITransparency = e.readBitField(uint(p.NumCustomPurposes)) - if p.SegmentType != 3 { - return nil, fmt.Errorf("allowed vendors segment type must be 3") + if p.SegmentType != int(SegmentTypePublisherTC) { + return nil, fmt.Errorf("publisher TC segment type must be %d", SegmentTypePublisherTC) } return p, nil diff --git a/segment_allowed_vendors.go b/segment_allowed_vendors.go index 2f52ed1..a9be738 100644 --- a/segment_allowed_vendors.go +++ b/segment_allowed_vendors.go @@ -29,19 +29,16 @@ func (a *AllowedVendors) IsVendorAllowed(id int) bool { // Returns structure as a base64 raw url encoded string func (a *AllowedVendors) Encode() string { - bitSize := 20 + var bitSize int + bitSize += bitsSegmentType + bitSize += bitsMaxVendorId + bitSize += bitsIsRangeEncoding if a.IsRangeEncoding { - bitSize += 12 - entriesSize := len(a.RangeEntries) + bitSize += bitsNumEntries for _, entry := range a.RangeEntries { - if entry.EndVendorID > entry.StartVendorID { - entriesSize += 16 * 2 - } else { - entriesSize += 16 - } + bitSize += entry.getBitSize() } - bitSize += entriesSize } else { if a.MaxVendorId == 0 { for id, _ := range a.AllowedVendors { @@ -53,21 +50,14 @@ func (a *AllowedVendors) Encode() string { bitSize += a.MaxVendorId } - var e = newTCEncoder(make([]byte, bitSize/8)) - if bitSize%8 != 0 { - e = newTCEncoder(make([]byte, bitSize/8+1)) - } - - e.writeInt(a.SegmentType, 3) - e.writeInt(a.MaxVendorId, 16) + e := newTCEncoderFromSize(bitSize) + e.writeInt(a.SegmentType, bitsSegmentType) + e.writeInt(a.MaxVendorId, bitsMaxVendorId) e.writeBool(a.IsRangeEncoding) if a.IsRangeEncoding { - e.writeInt(len(a.RangeEntries), 12) e.writeRangeEntries(a.RangeEntries) } else { - for i := 0; i < a.MaxVendorId; i++ { - e.writeBool(a.IsVendorAllowed(i + 1)) - } + e.writeBools(a.IsVendorAllowed, a.MaxVendorId) } return base64.RawURLEncoding.EncodeToString(e.bytes) diff --git a/segment_core_string.go b/segment_core_string.go index 1a90856..0141059 100644 --- a/segment_core_string.go +++ b/segment_core_string.go @@ -43,11 +43,31 @@ type PubRestriction struct { RangeEntries []*RangeEntry } +func (r *PubRestriction) getBitSize() (bitSize int) { + bitSize += bitsPubRestrictionsEntryPurposeId + bitSize += bitsPubRestrictionsEntryRestrictionType + bitSize += bitsNumEntries + for _, entry := range r.RangeEntries { + bitSize += entry.getBitSize() + } + return bitSize +} + type RangeEntry struct { StartVendorID int EndVendorID int } +func (r *RangeEntry) getBitSize() (bitSize int) { + bitSize += bitsIsRangeEncoding + if r.EndVendorID > r.StartVendorID { + bitSize += bitsVendorId * 2 + } else { + bitSize += bitsVendorId + } + return bitSize +} + // Returns true if user has given consent to special feature id func (c *CoreString) IsSpecialFeatureAllowed(id int) bool { return c.SpecialFeatureOptIns[id] @@ -244,19 +264,31 @@ func (p *PubRestriction) IsVendorIncluded(id int) bool { // Returns structure as a base64 raw url encoded string func (c *CoreString) Encode() string { - bitSize := 230 - + var bitSize int + bitSize += bitsVersion + bitSize += bitsCreated + bitSize += bitsLastUpdated + bitSize += bitsCmpId + bitSize += bitsCmpVersion + bitSize += bitsConsentScreen + bitSize += bitsConsentLanguage + bitSize += bitsVendorListVersion + bitSize += bitsTcfPolicyVersion + bitSize += bitsIsServiceSpecific + bitSize += bitsUseNonStandardTexts + bitSize += bitsSpecialFeatureOptIns + bitSize += bitsPurposesConsent + bitSize += bitsPurposesLITransparency + bitSize += bitsPurposeOneTreatment + bitSize += bitsPublisherCC + + bitSize += bitsMaxVendorId + bitSize += bitsIsRangeEncoding if c.IsRangeEncoding { - bitSize += 12 - entriesSize := len(c.RangeEntries) + bitSize += bitsNumEntries for _, entry := range c.RangeEntries { - if entry.EndVendorID > entry.StartVendorID { - entriesSize += 16 * 2 - } else { - entriesSize += 16 - } + bitSize += entry.getBitSize() } - bitSize += +entriesSize } else { if c.MaxVendorId == 0 { for id, _ := range c.VendorsConsent { @@ -268,18 +300,13 @@ func (c *CoreString) Encode() string { bitSize += c.MaxVendorId } - bitSize += 16 + bitSize += bitsMaxVendorId + bitSize += bitsIsRangeEncoding if c.IsRangeEncodingLI { - bitSize += 12 - entriesSize := len(c.RangeEntriesLI) + bitSize += bitsNumEntries for _, entry := range c.RangeEntriesLI { - if entry.EndVendorID > entry.StartVendorID { - entriesSize += 16 * 2 - } else { - entriesSize += 16 - } + bitSize += entry.getBitSize() } - bitSize += entriesSize } else { if c.MaxVendorIdLI == 0 { for id, _ := range c.VendorsLITransparency { @@ -291,71 +318,45 @@ func (c *CoreString) Encode() string { bitSize += c.MaxVendorIdLI } - bitSize += 12 - for _, res := range c.PubRestrictions { - entriesSize := 20 - for _, entry := range res.RangeEntries { - entriesSize++ - if entry.EndVendorID > entry.StartVendorID { - entriesSize += 16 * 2 - } else { - entriesSize += 16 - } - } - bitSize += entriesSize + bitSize += bitsNumPubRestrictions + for _, restriction := range c.PubRestrictions { + bitSize += restriction.getBitSize() } - var e = newTCEncoder(make([]byte, bitSize/8)) - if bitSize%8 != 0 { - e = newTCEncoder(make([]byte, bitSize/8+1)) - } - - e.writeInt(c.Version, 6) + e := newTCEncoderFromSize(bitSize) + e.writeInt(c.Version, bitsVersion) e.writeTime(c.Created) e.writeTime(c.LastUpdated) - e.writeInt(c.CmpId, 12) - e.writeInt(c.CmpVersion, 12) - e.writeInt(c.ConsentScreen, 6) - e.writeIsoCode(c.ConsentLanguage) - e.writeInt(c.VendorListVersion, 12) - e.writeInt(c.TcfPolicyVersion, 6) + e.writeInt(c.CmpId, bitsCmpId) + e.writeInt(c.CmpVersion, bitsCmpVersion) + e.writeInt(c.ConsentScreen, bitsConsentScreen) + e.writeChars(c.ConsentLanguage, bitsConsentLanguage) + e.writeInt(c.VendorListVersion, bitsVendorListVersion) + e.writeInt(c.TcfPolicyVersion, bitsTcfPolicyVersion) e.writeBool(c.IsServiceSpecific) e.writeBool(c.UseNonStandardStacks) - for i := 0; i < 12; i++ { - e.writeBool(c.IsSpecialFeatureAllowed(i + 1)) - } - for i := 0; i < 24; i++ { - e.writeBool(c.IsPurposeAllowed(i + 1)) - } - for i := 0; i < 24; i++ { - e.writeBool(c.IsPurposeLIAllowed(i + 1)) - } + e.writeBools(c.IsSpecialFeatureAllowed, bitsSpecialFeatureOptIns) + e.writeBools(c.IsPurposeAllowed, bitsPurposesConsent) + e.writeBools(c.IsPurposeLIAllowed, bitsPurposesLITransparency) e.writeBool(c.PurposeOneTreatment) - e.writeIsoCode(c.PublisherCC) + e.writeChars(c.PublisherCC, bitsPublisherCC) - e.writeInt(c.MaxVendorId, 16) + e.writeInt(c.MaxVendorId, bitsMaxVendorId) e.writeBool(c.IsRangeEncoding) if c.IsRangeEncoding { - e.writeInt(len(c.RangeEntries), 12) e.writeRangeEntries(c.RangeEntries) } else { - for i := 0; i < c.MaxVendorId; i++ { - e.writeBool(c.IsVendorAllowed(i + 1)) - } + e.writeBools(c.IsVendorAllowed, c.MaxVendorId) } - e.writeInt(c.MaxVendorIdLI, 16) + e.writeInt(c.MaxVendorIdLI, bitsMaxVendorId) e.writeBool(c.IsRangeEncodingLI) if c.IsRangeEncodingLI { - e.writeInt(len(c.RangeEntriesLI), 12) e.writeRangeEntries(c.RangeEntriesLI) } else { - for i := 0; i < c.MaxVendorIdLI; i++ { - e.writeBool(c.IsVendorLIAllowed(i + 1)) - } + e.writeBools(c.IsVendorLIAllowed, c.MaxVendorIdLI) } - e.writeInt(len(c.PubRestrictions), 12) e.writePubRestrictions(c.PubRestrictions) return base64.RawURLEncoding.EncodeToString(e.bytes) diff --git a/segment_disclosed_vendors.go b/segment_disclosed_vendors.go index 4a464f2..65dc6c1 100644 --- a/segment_disclosed_vendors.go +++ b/segment_disclosed_vendors.go @@ -29,19 +29,16 @@ func (d *DisclosedVendors) IsVendorDisclosed(id int) bool { // Returns structure as a base64 raw url encoded string func (d *DisclosedVendors) Encode() string { - bitSize := 20 + var bitSize int + bitSize += bitsSegmentType + bitSize += bitsMaxVendorId + bitSize += bitsIsRangeEncoding if d.IsRangeEncoding { - bitSize += 12 - entriesSize := len(d.RangeEntries) + bitSize += bitsNumEntries for _, entry := range d.RangeEntries { - if entry.EndVendorID > entry.StartVendorID { - entriesSize += 16 * 2 - } else { - entriesSize += 16 - } + bitSize += entry.getBitSize() } - bitSize += entriesSize } else { if d.MaxVendorId == 0 { for id, _ := range d.DisclosedVendors { @@ -53,21 +50,14 @@ func (d *DisclosedVendors) Encode() string { bitSize += d.MaxVendorId } - var e = newTCEncoder(make([]byte, bitSize/8)) - if bitSize%8 != 0 { - e = newTCEncoder(make([]byte, bitSize/8+1)) - } - - e.writeInt(d.SegmentType, 3) - e.writeInt(d.MaxVendorId, 16) + e := newTCEncoderFromSize(bitSize) + e.writeInt(d.SegmentType, bitsSegmentType) + e.writeInt(d.MaxVendorId, bitsMaxVendorId) e.writeBool(d.IsRangeEncoding) if d.IsRangeEncoding { - e.writeInt(len(d.RangeEntries), 12) e.writeRangeEntries(d.RangeEntries) } else { - for i := 0; i < d.MaxVendorId; i++ { - e.writeBool(d.IsVendorDisclosed(i + 1)) - } + e.writeBools(d.IsVendorDisclosed, d.MaxVendorId) } return base64.RawURLEncoding.EncodeToString(e.bytes) diff --git a/segment_publisher_tc.go b/segment_publisher_tc.go index 8a454f3..b9f5706 100644 --- a/segment_publisher_tc.go +++ b/segment_publisher_tc.go @@ -35,27 +35,21 @@ func (p *PublisherTC) IsCustomPurposeLIAllowed(id int) bool { // Returns structure as a base64 raw url encoded string func (p *PublisherTC) Encode() string { - bitSize := 57 + (p.NumCustomPurposes * 2) - - var e = newTCEncoder(make([]byte, bitSize/8)) - if bitSize%8 != 0 { - e = newTCEncoder(make([]byte, bitSize/8+1)) - } - - e.writeInt(p.SegmentType, 3) - for i := 0; i < 24; i++ { - e.writeBool(p.IsPurposeAllowed(i + 1)) - } - for i := 0; i < 24; i++ { - e.writeBool(p.IsPurposeLIAllowed(i + 1)) - } - e.writeInt(p.NumCustomPurposes, 6) - for i := 0; i < p.NumCustomPurposes; i++ { - e.writeBool(p.IsCustomPurposeAllowed(i + 1)) - } - for i := 0; i < p.NumCustomPurposes; i++ { - e.writeBool(p.IsCustomPurposeLIAllowed(i + 1)) - } + var bitSize int + bitSize += bitsSegmentType + + bitSize += bitsPubPurposesConsent + bitSize += bitsPubPurposesLITransparency + bitSize += bitsNumCustomPurposes + bitSize += p.NumCustomPurposes * 2 + + e := newTCEncoderFromSize(bitSize) + e.writeInt(p.SegmentType, bitsSegmentType) + e.writeBools(p.IsPurposeAllowed, bitsPubPurposesConsent) + e.writeBools(p.IsPurposeLIAllowed, bitsPubPurposesLITransparency) + e.writeInt(p.NumCustomPurposes, bitsNumCustomPurposes) + e.writeBools(p.IsCustomPurposeAllowed, p.NumCustomPurposes) + e.writeBools(p.IsCustomPurposeLIAllowed, p.NumCustomPurposes) return base64.RawURLEncoding.EncodeToString(e.bytes) } diff --git a/tcencoder.go b/tcencoder.go index e602320..928c93f 100644 --- a/tcencoder.go +++ b/tcencoder.go @@ -17,26 +17,34 @@ func newTCEncoder(src []byte) *TCEncoder { return &TCEncoder{newBits(src)} } +func newTCEncoderFromSize(bitSize int) *TCEncoder { + if bitSize%8 != 0 { + return newTCEncoder(make([]byte, bitSize/8+1)) + } + return newTCEncoder(make([]byte, bitSize/8)) +} + func (r *TCEncoder) readTime() time.Time { - var ds = int64(r.readInt(36)) + var ds = int64(r.readInt(bitsTime)) return time.Unix(ds/decisecondsPerSecond, (ds%decisecondsPerSecond)*nanosecondsPerDecisecond).UTC() } func (r *TCEncoder) writeTime(v time.Time) { - r.writeNumber(v.UnixNano()/nanosecondsPerDecisecond, 36) + r.writeNumber(v.UnixNano()/nanosecondsPerDecisecond, bitsTime) } -func (r *TCEncoder) readIsoCode() string { - var buf = make([]byte, 0, 2) - for i := uint(0); i < 2; i++ { - buf = append(buf, byte(r.readInt(6))+'A') +func (r *TCEncoder) readChars(n uint) string { + var buf = make([]byte, 0, n/bitsChar) + for i := uint(0); i < n/bitsChar; i++ { + buf = append(buf, byte(r.readInt(bitsChar))+'A') } return string(buf) } -func (r *TCEncoder) writeIsoCode(v string) { - for _, char := range v { - r.writeInt(int(byte(char)-'A'), 6) +func (r *TCEncoder) writeChars(v string, n uint) { + for i := uint(0); i < n/bitsChar; i++ { + char := v[i] + r.writeInt(int(byte(char)-'A'), bitsChar) } } @@ -50,56 +58,64 @@ func (r *TCEncoder) readBitField(n uint) map[int]bool { return m } +func (b *Bits) writeBools(getBool func(int) bool, n int) { + for i := 1; i <= n; i++ { + b.writeBool(getBool(i)) + } +} + func (r *TCEncoder) writeRangeEntries(entries []*RangeEntry) { + r.writeInt(len(entries), bitsNumEntries) for _, entry := range entries { if entry.EndVendorID > entry.StartVendorID { r.writeBool(true) - r.writeInt(entry.StartVendorID, 16) - r.writeInt(entry.EndVendorID, 16) + r.writeInt(entry.StartVendorID, bitsVendorId) + r.writeInt(entry.EndVendorID, bitsVendorId) } else { r.writeBool(false) - r.writeInt(entry.StartVendorID, 16) + r.writeInt(entry.StartVendorID, bitsVendorId) } } } -func (r *TCEncoder) readRangeEntries(n uint) []*RangeEntry { +func (r *TCEncoder) readRangeEntries() (int, []*RangeEntry) { + n := r.readInt(bitsNumEntries) var ret = make([]*RangeEntry, 0, n) - for i := uint(0); i < n; i++ { + for i := uint(0); i < uint(n); i++ { var isRange = r.readBool() var start, end int - start = r.readInt(16) + start = r.readInt(bitsVendorId) if isRange { - end = r.readInt(16) + end = r.readInt(bitsVendorId) } else { end = start } ret = append(ret, &RangeEntry{StartVendorID: start, EndVendorID: end}) } - return ret + return n, ret } func (r *TCEncoder) writePubRestrictions(entries []*PubRestriction) { + r.writeInt(len(entries), bitsNumPubRestrictions) for _, entry := range entries { - r.writeInt(entry.PurposeId, 6) - r.writeInt(int(entry.RestrictionType), 2) - r.writeInt(len(entry.RangeEntries), 12) + r.writeInt(entry.PurposeId, bitsPubRestrictionsEntryPurposeId) + r.writeInt(int(entry.RestrictionType), bitsPubRestrictionsEntryRestrictionType) r.writeRangeEntries(entry.RangeEntries) } } -func (r *TCEncoder) readPubRestrictions(n uint) []*PubRestriction { +func (r *TCEncoder) readPubRestrictions() (int, []*PubRestriction) { + n := r.readInt(bitsNumPubRestrictions) var ret = make([]*PubRestriction, 0, n) - for i := uint(0); i < n; i++ { - var purposeId = r.readInt(6) - var restrictionType = r.readInt(2) - var numEntries = r.readInt(12) - var rangeEntries = r.readRangeEntries(uint(numEntries)) + for i := uint(0); i < uint(n); i++ { + var purposeId = r.readInt(bitsPubRestrictionsEntryPurposeId) + var restrictionType = r.readInt(bitsPubRestrictionsEntryRestrictionType) + numEntries, rangeEntries := r.readRangeEntries() ret = append(ret, &PubRestriction{PurposeId: purposeId, RestrictionType: RestrictionType(restrictionType), NumEntries: numEntries, RangeEntries: rangeEntries, }) } - return ret + return n, ret }