github.com/qioalice/ekago/v3@v3.3.2-0.20221202205325-5c262d586ee4/ekaerr/class_private.go (about) 1 // Copyright © 2020. All rights reserved. 2 // Author: Ilya Stroy. 3 // Contacts: iyuryevich@pm.me, https://github.com/qioalice 4 // License: https://opensource.org/licenses/MIT 5 6 package ekaerr 7 8 import ( 9 "sync" 10 ) 11 12 //noinspection GoSnakeCaseUsage 13 14 const ( 15 // _ERR_CLASS_ARRAY_CACHE describes how many registered Classes will be 16 // stored into internal array instead of map. 17 // See registeredClassesArr, registeredClassesMap and classByID() for mor details. 18 _ERR_CLASS_ARRAY_CACHE = 128 19 ) 20 21 var ( 22 // invalidClass is a special Class object that will be returned if we can not 23 // provide a natural Class object. For example at the: (*Error)(nil).Class(). 24 invalidClass = Class{id: _ERR_INVALID_CLASS_ID} 25 26 // registeredClassesArr is a registered Classes storage. 27 // First N classes (_ERR_CLASS_ARRAY_CACHE) will be saved into this array, 28 // others to the registeredClassesMap. 29 // 30 // Because array accessing faster than map's one it's an array, but 31 // user can define more than N classes. It's very rarely case, because 32 // I guess N is high enough but if that will happen, new classes will be stored 33 // into map. So, if in your app you have more than N error classes, guess 34 // performance array > map is not important to you. 35 // 36 // It's made to provide (*Error).Class() method working. 37 registeredClassesArr [_ERR_CLASS_ARRAY_CACHE]Class 38 39 // registeredClassesMap used to save registered Classes when you have their 40 // more than N (_ERR_CLASS_ARRAY_CACHE). 41 registeredClassesMap = struct { 42 sync.RWMutex 43 m map[ClassID]Class 44 }{ 45 m: make(map[ClassID]Class), 46 } 47 ) 48 49 // fullClassName generates and returns full Class's name 50 // using 'className' as Class's name, and 'namespaceName' as Namespace's 51 // (or get a Namespace object from the pool using 'namespaceID' if 'namespaceName' is empty). 52 func fullClassName(className, namespaceName string, namespaceID NamespaceID) string { 53 54 if namespaceName == "" { 55 namespaceName = namespaceByID(namespaceID, false).name 56 } 57 return namespaceName + "::" + className 58 } 59 60 // rebuiltExistedClassNames changes the names of existed (registered) Classes 61 // (in the pool) use fullClassName() for their full names. 62 // It guarantees that this function will be called only once when the first custom 63 // namespace will be created. 64 func rebuiltExistedClassNames() { 65 registeredClassesMap.Lock() 66 defer registeredClassesMap.Unlock() 67 68 for i, cls := range registeredClassesArr { 69 registeredClassesArr[i].fullName = 70 fullClassName(cls.fullName, "", cls.namespaceID) 71 } 72 73 for clsID, cls := range registeredClassesMap.m { 74 cls.fullName = fullClassName(cls.fullName, "", cls.namespaceID) 75 registeredClassesMap.m[clsID] = cls 76 } 77 } 78 79 // newClass is a Class's constructor. 80 // There are several steps: 81 // 82 // 1. Getting a new available Class's ID. 83 // 2. Create a new Class object using provided 'parentID', 'namespaceID', 'name'. 84 // 3. Save it into the internal Class's storage basing on the its (Class's) ID. 85 func newClass(parentID ClassID, namespaceID NamespaceID, name, fullName string) Class { 86 87 c := Class{ 88 id: newClassID(), 89 parentID: parentID, 90 namespaceID: namespaceID, 91 name: name, 92 fullName: fullName, 93 } 94 95 if c.id >= _ERR_CLASS_ARRAY_CACHE { 96 registeredClassesMap.Lock() 97 defer registeredClassesMap.Unlock() 98 registeredClassesMap.m[c.id] = c 99 } else { 100 registeredClassesArr[c.id] = c 101 } 102 103 return c 104 }