package fontface

import (
	
	
	
	

	gotextfont 
)

// D2 ships a fixed set of ordinary fonts. Keeping this ceiling independent of
// callers prevents this package-level registry from becoming an unbounded font
// cache if more registration sites are added later.
const maxBundledFaceSources = 16

type bundledFaceEntry struct {
	size   int
	digest [sha256.Size]byte

	mu      sync.RWMutex
	data    []byte
	sources []BundledFaceSource
	err     error
}

func ( *bundledFaceEntry) ( []byte) error {
	.mu.RLock()
	 := .sources != nil || .err != nil
	 := .err
	.mu.RUnlock()
	if  {
		return 
	}

	// Authenticate a private snapshot before acquiring the parse lock. A bad
	// candidate cannot poison the fixed registry, and the snapshot closes the
	// ownership boundary around the parsers' retained table slices.
	 := bytes.Clone()
	if len() != .size || sha256.Sum256() != .digest {
		return fmt.Errorf("bundled font resource is not authenticated")
	}

	.mu.Lock()
	defer .mu.Unlock()
	if .sources != nil || .err != nil {
		return .err
	}
	,  := parseFaceCollectionWithLimitUsingParserAndDigest(, maxParsedFontFaces, gotextfont.ParseTTC, .digest)
	if  != nil {
		.err = 
		return 
	}
	 := make([]BundledFaceSource, .NumFaces())
	for  := range  {
		,  := .Face()
		if  != nil {
			.err = 
			return 
		}
		.source.bundledOutline = true
		[].face = 
	}
	.data = 
	.sources = 
	return nil
}

var bundledFaceRegistry struct {
	sync.RWMutex
	entries  []*bundledFaceEntry
	byDigest map[[sha256.Size]byte]*bundledFaceEntry
	bySize   map[int][]*bundledFaceEntry
}

// RegisterBundledFace records the authenticated size and digest of a D2 font.
// It does not retain data. Parsing and a private backing copy are created lazily
// from the first matching candidate, so a public font registry cannot expose
// or mutate parser-owned tables. Callers must not register dynamic fonts.
func ( []byte,  [sha256.Size]byte) error {
	if len() == 0 ||  == ([sha256.Size]byte{}) {
		return fmt.Errorf("invalid bundled font registration")
	}
	bundledFaceRegistry.Lock()
	defer bundledFaceRegistry.Unlock()
	if bundledFaceRegistry.byDigest == nil {
		bundledFaceRegistry.byDigest = make(map[[sha256.Size]byte]*bundledFaceEntry)
		bundledFaceRegistry.bySize = make(map[int][]*bundledFaceEntry)
	}
	if  := bundledFaceRegistry.byDigest[];  != nil {
		if len() == .size {
			return nil
		}
		return fmt.Errorf("duplicate bundled font digest has size %d, want %d", len(), .size)
	}
	if len(bundledFaceRegistry.entries) >= maxBundledFaceSources {
		return fmt.Errorf("bundled font registry exceeds limit %d", maxBundledFaceSources)
	}
	 := &bundledFaceEntry{size: len(), digest: }
	bundledFaceRegistry.entries = append(bundledFaceRegistry.entries, )
	bundledFaceRegistry.byDigest[] = 
	bundledFaceRegistry.bySize[len()] = append(bundledFaceRegistry.bySize[len()], )
	return nil
}

// RegisteredBundledFace authenticates data against D2's fixed bundled-font
// registry. A non-match is not an error so arbitrary scene fonts continue down
// the ordinary bounded parser path.
func ( []byte,  uint16) (*BundledFaceSource, bool, error) {
	 := registeredBundledFaceEntry(, nil)
	return registeredBundledFaceSource(, , )
}

// RegisteredBundledFaceDigest is RegisteredBundledFace for callers that have
// already computed the source digest as part of their own bounded cache key.
func ( []byte,  uint16,  [sha256.Size]byte) (*BundledFaceSource, bool, error) {
	 := registeredBundledFaceEntry(, &)
	return registeredBundledFaceSource(, , )
}

// RegisteredBundledFaceBackingDigest identifies the exact process-owned
// backing of a registered font without scanning it. The source is still
// authenticated and dual-parsed by RegisteredBundledFaceDigest before use.
// Byte-identical copies deliberately do not match this identity-only path.
func ( []byte) ([sha256.Size]byte, bool) {
	if len() == 0 {
		return [sha256.Size]byte{}, false
	}
	bundledFaceRegistry.RLock()
	defer bundledFaceRegistry.RUnlock()
	for ,  := range bundledFaceRegistry.bySize[len()] {
		.mu.RLock()
		 := sameSlice(, .data)
		.mu.RUnlock()
		if  {
			return .digest, true
		}
	}
	return [sha256.Size]byte{}, false
}

func registeredBundledFaceEntry( []byte,  *[sha256.Size]byte) *bundledFaceEntry {
	if len() == 0 {
		return nil
	}
	var  [sha256.Size]byte
	if  == nil {
		bundledFaceRegistry.RLock()
		for ,  := range bundledFaceRegistry.bySize[len()] {
			.mu.RLock()
			 := .data
			.mu.RUnlock()
			if  != nil && (sameSlice(, ) || bytes.Equal(, )) {
				bundledFaceRegistry.RUnlock()
				return 
			}
		}
		bundledFaceRegistry.RUnlock()
		 = sha256.Sum256()
	} else {
		 = *
	}
	bundledFaceRegistry.RLock()
	 := bundledFaceRegistry.byDigest[]
	if  == nil || len() != .size {
		 = nil
	}
	bundledFaceRegistry.RUnlock()
	if  != nil &&  == nil {
		.mu.RLock()
		 := .data
		.mu.RUnlock()
		if  != nil && !sameSlice(, ) && !bytes.Equal(, ) {
			 = nil
		}
	}
	return 
}

func registeredBundledFaceSource( *bundledFaceEntry,  []byte,  uint16) (*BundledFaceSource, bool, error) {
	if  == nil {
		return nil, false, nil
	}
	if  := .load();  != nil {
		return nil, true, 
	}
	.mu.RLock()
	defer .mu.RUnlock()
	if int() >= len(.sources) {
		return nil, true, fmt.Errorf("load font face %d: collection has %d faces", , len(.sources))
	}
	return &.sources[], true, nil
}

func sameSlice(,  []byte) bool {
	return len() == len() && (len() == 0 || &[0] == &[0])
}