feat: sync langpack and auth updates

Sync telesrv commits 49f9bab and 04d9563 into the public mirror. Exclude private docs and runtime key material per sync rules.
This commit is contained in:
A 2026-07-18 00:07:41 +08:00
parent 6af61f26ba
commit 1292540350
244 changed files with 1552172 additions and 27668 deletions

View file

@ -0,0 +1,238 @@
package langpack
import (
"container/list"
"sync"
"telesrv/internal/domain"
)
const (
defaultLangPackCacheMaxBytes = int64(128 << 20)
defaultLangPackCacheMaxEntries = 256
defaultLanguageListCacheMaxEntries = 32
langPackStringValueHeaderBytes = int64(144)
langPackFixedHeaderBytes = int64(80)
)
type langPackCacheKind uint8
const (
langPackCacheRaw langPackCacheKind = iota
langPackCacheEffective
)
type langPackCacheKey struct {
pack string
code string
kind langPackCacheKind
}
func (k langPackCacheKey) singleflightKey() string {
return string(rune(k.kind)) + "\x00" + k.pack + "\x00" + k.code
}
type langPackCache struct {
mu sync.Mutex
maxBytes int64
maxEntries int
usedBytes int64
epoch uint64
ll *list.List
items map[langPackCacheKey]*list.Element
}
type langPackCacheEntry struct {
key langPackCacheKey
pack domain.LangPack
size int64
}
func newLangPackCache(maxBytes int64, maxEntries int) *langPackCache {
if maxBytes <= 0 || maxEntries <= 0 {
return nil
}
return &langPackCache{
maxBytes: maxBytes,
maxEntries: maxEntries,
ll: list.New(),
items: make(map[langPackCacheKey]*list.Element),
}
}
func (c *langPackCache) get(key langPackCacheKey) (domain.LangPack, bool) {
if c == nil {
return domain.LangPack{}, false
}
c.mu.Lock()
defer c.mu.Unlock()
element, ok := c.items[key]
if !ok {
return domain.LangPack{}, false
}
c.ll.MoveToFront(element)
return cloneLangPack(element.Value.(*langPackCacheEntry).pack), true
}
func (c *langPackCache) loadEpoch() uint64 {
if c == nil {
return 0
}
c.mu.Lock()
epoch := c.epoch
c.mu.Unlock()
return epoch
}
// putIfEpoch 返回 false 仅表示 load 期间发生过 flush,调用方必须重载。
// 超大单项不进入缓存,但仍可安全返回给当前请求,因此返回 true。
func (c *langPackCache) putIfEpoch(key langPackCacheKey, pack domain.LangPack, loadEpoch uint64) bool {
if c == nil {
return true
}
size := estimateLangPackBytes(pack)
c.mu.Lock()
defer c.mu.Unlock()
if c.epoch != loadEpoch {
return false
}
if existing, ok := c.items[key]; ok {
c.remove(existing)
}
if size > c.maxBytes {
return true
}
entry := &langPackCacheEntry{key: key, pack: cloneLangPack(pack), size: size}
c.items[key] = c.ll.PushFront(entry)
c.usedBytes += size
for c.usedBytes > c.maxBytes || c.ll.Len() > c.maxEntries {
oldest := c.ll.Back()
if oldest == nil {
break
}
c.remove(oldest)
}
return true
}
func (c *langPackCache) flush() {
if c == nil {
return
}
c.mu.Lock()
c.epoch++
c.usedBytes = 0
c.ll.Init()
clear(c.items)
c.mu.Unlock()
}
func (c *langPackCache) remove(element *list.Element) {
entry := element.Value.(*langPackCacheEntry)
delete(c.items, entry.key)
c.ll.Remove(element)
c.usedBytes -= entry.size
}
func estimateLangPackBytes(pack domain.LangPack) int64 {
size := langPackFixedHeaderBytes + int64(len(pack.LangPack)+len(pack.LangCode))
size += int64(len(pack.Strings)) * langPackStringValueHeaderBytes
for _, item := range pack.Strings {
size += int64(len(item.Key) + len(item.Value) + len(item.ZeroValue) + len(item.OneValue) +
len(item.TwoValue) + len(item.FewValue) + len(item.ManyValue) + len(item.OtherValue))
}
return size
}
func cloneLangPack(pack domain.LangPack) domain.LangPack {
pack.Strings = append([]domain.LangPackString(nil), pack.Strings...)
return pack
}
type languageListCache struct {
mu sync.Mutex
maxEntries int
epoch uint64
ll *list.List
items map[string]*list.Element
}
type languageListCacheEntry struct {
pack string
languages []domain.LangPackLanguage
}
func newLanguageListCache(maxEntries int) *languageListCache {
if maxEntries <= 0 {
return nil
}
return &languageListCache{
maxEntries: maxEntries,
ll: list.New(),
items: make(map[string]*list.Element),
}
}
func (c *languageListCache) get(pack string) ([]domain.LangPackLanguage, bool) {
if c == nil {
return nil, false
}
c.mu.Lock()
defer c.mu.Unlock()
element, ok := c.items[pack]
if !ok {
return nil, false
}
c.ll.MoveToFront(element)
return cloneLanguages(element.Value.(*languageListCacheEntry).languages), true
}
func (c *languageListCache) loadEpoch() uint64 {
if c == nil {
return 0
}
c.mu.Lock()
epoch := c.epoch
c.mu.Unlock()
return epoch
}
func (c *languageListCache) putIfEpoch(pack string, languages []domain.LangPackLanguage, loadEpoch uint64) bool {
if c == nil {
return true
}
c.mu.Lock()
defer c.mu.Unlock()
if c.epoch != loadEpoch {
return false
}
if existing, ok := c.items[pack]; ok {
c.ll.Remove(existing)
delete(c.items, pack)
}
entry := &languageListCacheEntry{pack: pack, languages: cloneLanguages(languages)}
c.items[pack] = c.ll.PushFront(entry)
if c.ll.Len() > c.maxEntries {
oldest := c.ll.Back()
if oldest != nil {
delete(c.items, oldest.Value.(*languageListCacheEntry).pack)
c.ll.Remove(oldest)
}
}
return true
}
func (c *languageListCache) flush() {
if c == nil {
return
}
c.mu.Lock()
c.epoch++
c.ll.Init()
clear(c.items)
c.mu.Unlock()
}
func cloneLanguages(languages []domain.LangPackLanguage) []domain.LangPackLanguage {
return append([]domain.LangPackLanguage(nil), languages...)
}