ccadb2OneCRL/set/set.go (164 lines of code) (raw):
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package set
import (
"reflect"
log "github.com/sirupsen/logrus"
)
type Record interface {
IssuerSerial() *IssuerSerial
SubjectKeyHash() *SubjectKeyHash
Type() Type
}
type Set interface {
Add(record Record)
Contains(record Record) bool
Iter() <-chan Record
Union(other Set) Set
Difference(other Set) Set
Intersection(other Set) Set
}
type Type int
const (
IssuerSerialType Type = iota
SubjectKeyHashType
Either
)
func (t *Type) String() string {
switch *t {
case IssuerSerialType:
return "IssuerSerial"
case SubjectKeyHashType:
return "SubjectKeyHash"
case Either:
return "Either"
}
log.Panic()
return ""
}
// SetImpl holds a mapping of IssuerSerial -> Record and a mapping SubjectKeyHash -> Record
// and provides a logical, singular, view into both datasets.
//
// For example, if you wish to find if a Record is within a SetImpl then the provided record
// will be asked for its type. If it is an IssuerSerialType then the issuerSerial map will be
// checked. If it is a SubjectKeyHashType, then the subjectKeyHash map will be checked. If it
// is Either then both will be checked.
//
// Consumers of this struct SHOULD embed a *SetImpl and override the Add method if they wish to make
// the underlying Records homogenous.
//
// SetImpl has a dependency injection requirement on a factory function for a specific type of Set.
// This is required as the set operations (Union, Difference, and Intersection) need to return a new
// Set
type SetImpl struct {
setFactory func() Set
issuerSerial map[IssuerSerial]Record
subjectKeyHash map[SubjectKeyHash]Record
}
func NewSetImpl(setFactory func() Set) *SetImpl {
return &SetImpl{
setFactory: setFactory,
issuerSerial: make(map[IssuerSerial]Record),
subjectKeyHash: make(map[SubjectKeyHash]Record),
}
}
func NewDynamicSetImpl() *SetImpl {
return NewSetImpl(func() Set {
return NewDynamicSetImpl()
})
}
// Add will ATTEMPT to add the provided to record to the set.
// If the record cannot serialize itself into the appropriate type
// (IsserSerial:SubjectKeyHash) then it will be silent ignored.
// Implementors of Record SHOULD log errors this case as implementors
// are much closer to the data and can provide more meaningful messages
// than can be accomplished in this stack frame.
//
// The reason why this is an attemp is because there are entries within
// staging that are junk data that result in a B64 or ASN1 decoding error.
// We would HOPE that "real" entries aren't going to suffer from
// this, however we have not way to tell which entries are
// test data and which are destined for production. I
// suppose if an entry doesn't show up on Bugzilla, but you
// see it logged, then we know why.
func (s *SetImpl) Add(record Record) {
switch record.Type() {
case IssuerSerialType:
is := record.IssuerSerial()
if is == nil {
return
}
s.issuerSerial[*is] = record
case SubjectKeyHashType:
skh := record.SubjectKeyHash()
if skh == nil {
return
}
s.subjectKeyHash[*skh] = record
case Either:
is := record.IssuerSerial()
if is == nil {
return
}
skh := record.SubjectKeyHash()
if skh == nil {
return
}
s.issuerSerial[*is] = record
s.subjectKeyHash[*skh] = record
default:
log.WithField("record", record).
WithField("type", record.Type()).
Panic("unknown record type")
}
}
func (s *SetImpl) Get(record Record) Record {
switch record.Type() {
case IssuerSerialType:
is := record.IssuerSerial()
if is == nil {
return nil
}
if v, ok := s.issuerSerial[*is]; ok {
return v
}
case SubjectKeyHashType:
skh := record.SubjectKeyHash()
if skh == nil {
return nil
}
if v, ok := s.subjectKeyHash[*skh]; ok {
return v
}
case Either:
is := record.IssuerSerial()
if is != nil {
if v, ok := s.issuerSerial[*is]; ok {
return v
}
}
skh := record.SubjectKeyHash()
if is != nil {
if v, ok := s.subjectKeyHash[*skh]; ok {
return v
}
}
}
return nil
}
func (s *SetImpl) Iter() <-chan Record {
// We have two backing maps that can both
// hold a pointer to the same record, so
// we need to "flatten" that view by
// ensuring uniqueness via said pointer.
m := make(map[uintptr]Record)
for _, v := range s.subjectKeyHash {
m[reflect.ValueOf(v).Pointer()] = v
}
for _, v := range s.issuerSerial {
m[reflect.ValueOf(v).Pointer()] = v
}
ret := make(chan Record, len(m))
defer close(ret)
for _, v := range m {
ret <- v
}
return ret
}
func (s *SetImpl) Contains(record Record) bool {
return s.Get(record) != nil
}
// Union returns the unino of self and other. If self and other
// are homogenous and of same type, then the returned Set will
// also be homogenous of the same type. Otherwise the returned set
// will be heterogeneous.
//
// If the Set returned by the set factory injected into this struct
// enforces type checking then this method will likely panic in the
// heterogeneous case.
func (s *SetImpl) Union(other Set) Set {
union := s.setFactory()
for r := range s.Iter() {
union.Add(r)
}
for r := range other.Iter() {
union.Add(r)
}
return union
}
// Difference returns a Set of all Records that are in self
// but are NOT in other. If self is homogenous then the returned
// Set will be homogenous and off the same type as self.
func (s *SetImpl) Difference(other Set) Set {
difference := s.setFactory()
for r := range s.Iter() {
if !other.Contains(r) {
difference.Add(r)
}
}
return difference
}
// Intersection returns a Set of all Records that are both in self
// AND in other. If self is homogenous then the returned
//// Set will be homogenous and off the same type as self.
func (s *SetImpl) Intersection(other Set) Set {
intersection := s.setFactory()
for r := range s.Iter() {
if other.Contains(r) {
intersection.Add(r)
}
}
return intersection
}