github.com/GeniusesGroup/libgo@v0.0.0-20220929090155-5ff932cb408e/pehrest/hash.go (about) 1 /* For license and copyright information please see LEGAL file in repository */ 2 3 package pehrest 4 5 import ( 6 "../convert" 7 "../time" 8 "../ganjine" 9 "../mediatype" 10 "../os/persiaos" 11 "../protocol" 12 "../syllab" 13 ) 14 15 const indexHashStructureID uint64 = 1404190754065006447 16 17 var IndexHashStructure = indexHashStructure{ 18 MediaType: mediatype.New("urn:giti:index.protocol:data-structure:hash", "application/vnd.index.protocol.hash", "", ""). 19 SetInfo(protocol.Software_PreAlpha, 1599286551, IndexHash{}). 20 SetDetail(protocol.LanguageEnglish, "Index Hash", 21 "Store the hash index data by 32byte key as ObjectID and any 16byte value", 22 []string{}), 23 } 24 25 type indexHashStructure struct { 26 mediatype.MediaType 27 } 28 29 // IndexHash is standard structure to store any hash byte index!! 30 // It is simple secondary index e.g. hash(RecordStructureID, "user@email.com") 31 type IndexHash struct { 32 WriteTime time.Time 33 EarlierExpandTime time.Time 34 LastExpandTime time.Time 35 IndexValuesNumber uint64 // IndexValues len 36 IndexValuesCapacity uint64 // IndexValues cap 37 IndexValues [][16]byte // Can store RecordsID or any data up to 16 byte length and store by time of added to index 38 } 39 40 // ReadHeader get needed data from storage and decode to given ih without IndexValues array data 41 func (ih *IndexHash) ReadHeader() (err protocol.Error) { 42 // Get first cluster of record to read header data 43 var header []byte 44 header, err = persiaos.StorageReadRecord(ih.RecordID, indexHashStructureID, 0, uint64(ih.LenOfSyllabStack())) 45 if err != nil { 46 return 47 } 48 49 err = ih.FromSyllab(header) 50 if err != nil { 51 return 52 } 53 54 return 55 } 56 57 // Pop return last RecordID in given index and delete it from index! 58 func (ih *IndexHash) Pop() (recordID [32]byte, err protocol.Error) { 59 // Get first cluster of record to read header data 60 var header []byte 61 header, err = persiaos.StorageReadRecord(ih.RecordID, indexHashStructureID, 0, uint64(ih.LenOfSyllabStack())) 62 if err != nil { 63 return 64 } 65 66 err = ih.FromSyllab(header) 67 if err != nil { 68 return 69 } 70 71 if ih.IndexValuesNumber > 1 { 72 ih.IndexValuesNumber-- 73 var record []byte 74 var recordOffset = uint64(ih.LenOfSyllabStack()) + (ih.IndexValuesNumber * 32) 75 record, err = persiaos.StorageReadRecord(ih.RecordID, indexHashStructureID, recordOffset, 32) 76 if err != nil { 77 return 78 } 79 copy(recordID[:], record[:]) 80 81 ih.WriteTime = time.Now() 82 var buf = make([]byte, ih.LenOfSyllabStack()) 83 ih.ToSyllabHeader(buf) 84 err = persiaos.StorageWriteRecord(ih.RecordID, indexHashStructureID, 0, buf) 85 err = persiaos.StorageWriteRecord(ih.RecordID, indexHashStructureID, recordOffset, make([]byte, 32)) 86 } else { 87 err = persiaos.StorageDeleteRecord(ih.RecordID, indexHashStructureID) 88 // err = ErrEndOfIndexRecord 89 } 90 91 return 92 } 93 94 // Peek return last recordID pushed to given index. unlike Pop() it won't delete it from index! 95 func (ih *IndexHash) Peek() (recordID [32]byte, err protocol.Error) { 96 // Get first cluster of record to read header data 97 var header []byte 98 header, err = persiaos.StorageReadRecord(ih.RecordID, indexHashStructureID, 0, uint64(ih.LenOfSyllabStack())) 99 if err != nil { 100 return 101 } 102 103 err = ih.FromSyllab(header) 104 if err != nil { 105 return 106 } 107 108 var record []byte 109 var recordOffset = uint64(ih.LenOfSyllabStack()) + ((ih.IndexValuesNumber - 1) * 32) 110 record, err = persiaos.StorageReadRecord(ih.RecordID, indexHashStructureID, recordOffset, 32) 111 if err != nil { 112 return 113 } 114 copy(recordID[:], record[:]) 115 116 return 117 } 118 119 // Get return related records ID to given index with offset and limit! 120 func (ih *IndexHash) Get(offset, limit uint64) (indexValues [][32]byte, err protocol.Error) { 121 // Get first cluster of record to read header data 122 var header []byte 123 header, err = persiaos.StorageReadRecord(ih.RecordID, indexHashStructureID, 0, uint64(ih.LenOfSyllabStack())) 124 if err != nil { 125 err = ganjine.ErrRecordNotFound 126 return 127 } 128 129 err = ih.FromSyllab(header) 130 if err != nil { 131 return 132 } 133 134 if limit > ih.IndexValuesNumber { 135 offset = 0 136 limit = ih.IndexValuesNumber 137 } else { 138 if offset > ih.IndexValuesNumber { 139 // If offset set to higher than exiting number of record always return last records by given limit! 140 offset = ih.IndexValuesNumber - limit 141 } 142 143 if limit == 0 { 144 if offset == 0 { 145 // it means returns all available IndexValues 146 limit = ih.IndexValuesNumber 147 } else { 148 // it means returns all available IndexValues after offset 149 limit = ih.IndexValuesNumber - offset 150 } 151 } 152 } 153 154 offset = uint64(ih.LenOfSyllabStack()) + (offset * 32) 155 limit = limit * 32 156 var record []byte 157 record, err = persiaos.StorageReadRecord(ih.RecordID, indexHashStructureID, offset, limit) 158 if err != nil { 159 return 160 } 161 162 // We know e.g. 128*[32]byte == [4096]byte is same size but Go is safe default and don't let return simply, so: 163 indexValues = convert.UnsafeByteSliceTo32ByteArraySlice(record) 164 return 165 } 166 167 // Push add given RecordID to then end of given hash index! 168 func (ih *IndexHash) Push(recordID [32]byte) (err protocol.Error) { 169 var timeNow = time.Now() 170 171 // Get first cluster of record to read header data 172 var record []byte 173 record, err = persiaos.StorageReadRecord(ih.RecordID, indexHashStructureID, 0, uint64(ih.LenOfSyllabStack())) 174 if err != nil { 175 if err.Equal(object.ErrNotExist) { 176 // desire record not found. write new one! 177 ih.RecordStructureID = indexHashStructureID 178 ih.RecordSize = uint64(ih.LenOfSyllabStack()) + (1 * 32) 179 ih.WriteTime = timeNow 180 ih.OwnerAppID = protocol.OS.AppManifest().AppUUID() 181 // ih.EarlierExpandTime = timeNow 182 ih.LastExpandTime = timeNow 183 ih.IndexValuesNumber = 1 184 ih.IndexValuesCapacity = 1 185 ih.IndexValues = make([][32]byte, 1) 186 ih.IndexValues[0] = recordID 187 err = persiaos.StorageSetRecord(ih.ToSyllabFull()) 188 } else { 189 // err = 190 } 191 return 192 } 193 194 err = ih.FromSyllab(record) 195 if err != nil { 196 return 197 } 198 199 // Check and expand record if needed 200 err = ih.checkAndExpand(timeNow) 201 if err != nil { 202 return 203 } 204 205 persiaos.StorageWriteRecord(ih.RecordID, indexHashStructureID, uint64(ih.LenOfSyllabStack())+(ih.IndexValuesNumber*32), recordID[:]) 206 207 ih.WriteTime = timeNow 208 ih.IndexValuesNumber++ 209 ih.ToSyllabHeader(record) 210 err = persiaos.StorageWriteRecord(ih.RecordID, indexHashStructureID, 0, record) 211 return 212 } 213 214 // Insert add given RecordID to the end of given hash index and return error if it is exist already! 215 func (ih *IndexHash) Insert(recordID [32]byte) (err protocol.Error) { 216 var timeNow = time.Now() 217 218 var record []byte 219 record, err = persiaos.StorageGetRecord(ih.RecordID, indexHashStructureID) 220 if err != nil { 221 if err.Equal(object.ErrNotExist) { 222 // desire record not found. write new one! 223 ih.RecordStructureID = indexHashStructureID 224 ih.RecordSize = uint64(ih.LenOfSyllabStack()) + (1 * 32) 225 ih.WriteTime = timeNow 226 ih.OwnerAppID = protocol.OS.AppManifest().AppUUID() 227 // ih.EarlierExpandTime = timeNow 228 ih.LastExpandTime = timeNow 229 ih.IndexValuesNumber = 1 230 ih.IndexValuesCapacity = 1 231 ih.IndexValues = make([][32]byte, 1) 232 ih.IndexValues[0] = recordID 233 err = persiaos.StorageSetRecord(ih.ToSyllabFull()) 234 } else { 235 // err = 236 } 237 return 238 } 239 240 err = ih.FromSyllab(record) 241 if err != nil { 242 return 243 } 244 245 for _, value := range ih.IndexValues { 246 if recordID == value { 247 err = ErrIndexValueAlreadyExist 248 return 249 } 250 } 251 252 // Check and expand record if needed 253 err = ih.checkAndExpand(timeNow) 254 if err != nil { 255 return 256 } 257 258 persiaos.StorageWriteRecord(ih.RecordID, indexHashStructureID, uint64(ih.LenOfSyllabStack())+(ih.IndexValuesNumber*32), recordID[:]) 259 260 ih.WriteTime = timeNow 261 ih.IndexValuesNumber++ 262 263 var newRecord = record[:ih.LenOfSyllabStack()] 264 ih.ToSyllabHeader(newRecord) 265 err = persiaos.StorageWriteRecord(ih.RecordID, indexHashStructureID, 0, newRecord) 266 return 267 } 268 269 // InsertInOrder add given RecordID to the given hash index in order and return error if it is exist already! 270 func (ih *IndexHash) InsertInOrder(recordID [32]byte) (err protocol.Error) { 271 272 return 273 } 274 275 // Check and expand record if needed 276 func (ih *IndexHash) checkAndExpand(timeNow time.Time) (err protocol.Error) { 277 if ih.IndexValuesNumber == ih.IndexValuesCapacity { 278 var expandNumber = ih.calculateExpandNumber(timeNow) 279 var expandSize = expandNumber * 32 280 ih.RecordSize += expandSize 281 ih.EarlierExpandTime = ih.LastExpandTime 282 ih.LastExpandTime = timeNow 283 ih.IndexValuesCapacity += expandNumber 284 err = persiaos.StorageExpandRecord(ih.RecordID, indexHashStructureID, expandSize) 285 if err != nil { 286 return 287 } 288 } 289 return 290 } 291 292 // TODO::: Improve expand algorithm 293 func (ih *IndexHash) calculateExpandNumber(timeNow time.Time) (expandNumber uint64) { 294 var ln = ih.IndexValuesCapacity 295 if ih.LastExpandTime-ih.EarlierExpandTime < (60 * 60) { // Expanded twice in less than 60 minutes 296 if timeNow < ih.LastExpandTime+(60*60) { // Last expand earlier than 60 minutes 297 expandNumber = ln 298 } else if timeNow < ih.LastExpandTime+(24*60*60) { // Last expand earlier than 1 day 299 expandNumber = ln / 2 300 } else { // Last expand more than 1 day 301 expandNumber = 8 302 } 303 } else if ih.LastExpandTime-ih.EarlierExpandTime < (24 * 60 * 60) { // Expanded twice in less than 1 day 304 if timeNow < ih.LastExpandTime+(24*60*60) { // Last expand earlier than 1 day 305 expandNumber = ln / 4 306 } else { // Last expand more than 1 day 307 expandNumber = 4 308 } 309 } else if ih.LastExpandTime-ih.EarlierExpandTime < (7 * 24 * 60 * 60) { // Expanded twice in less than 1 week 310 if timeNow < ih.LastExpandTime+(24*60*60) { // Last expand earlier than 1 day 311 expandNumber = ln / 8 312 } else { // Last expand more than 1 day 313 expandNumber = 1 314 } 315 } 316 if expandNumber == 0 { // Usually means expanded twice in more than 1 week 317 expandNumber = 1 318 } 319 return 320 } 321 322 // Append add given RecordID with any logic need like expand! 323 func (ih *IndexHash) Append(recordID ...[32]byte) (err protocol.Error) { 324 return 325 } 326 327 // DeleteRecord use to delete given record ID form given indexHash! 328 func (ih *IndexHash) DeleteRecord() (err protocol.Error) { 329 // Do for i=0 as local node 330 err = persiaos.StorageDeleteRecord(ih.RecordID, indexHashStructureID) 331 return 332 } 333 334 // Delete use to delete given record ID form given indexHash! 335 func (ih *IndexHash) Delete(recordID [32]byte) (err protocol.Error) { 336 var record []byte 337 record, err = persiaos.StorageGetRecord(ih.RecordID, indexHashStructureID) 338 if err != nil { 339 return 340 } 341 342 err = ih.FromSyllab(record) 343 if err != nil { 344 return 345 } 346 347 var ln = len(ih.IndexValues) 348 for i := 0; i < ln; i++ { 349 if ih.IndexValues[i] == recordID { 350 copy(ih.IndexValues[i:], ih.IndexValues[i+1:]) 351 ih.IndexValuesNumber-- 352 } 353 } 354 for i := uint64(len(ih.IndexValues) - 1); i >= ih.IndexValuesNumber; i-- { 355 ih.IndexValues[i] = [32]byte{} 356 } 357 358 err = persiaos.StorageSetRecord(ih.ToSyllabFull()) 359 return 360 } 361 362 // Deletes use to delete given records ID form given indexHash! 363 func (ih *IndexHash) Deletes(indexValues [][32]byte) (err protocol.Error) { 364 // Get first cluster of record to read header data 365 var record []byte 366 record, err = persiaos.StorageGetRecord(ih.RecordID, indexHashStructureID) 367 if err != nil { 368 return 369 } 370 371 err = ih.FromSyllab(record) 372 if err != nil { 373 return 374 } 375 376 var ln = len(ih.IndexValues) 377 var inputLn = len(indexValues) 378 for i := 0; i < ln; i++ { 379 for j := 0; j < inputLn; j++ { 380 if ih.IndexValues[i] == indexValues[j] { 381 copy(ih.IndexValues[i:], ih.IndexValues[i+1:]) 382 ih.IndexValuesNumber-- 383 } 384 } 385 } 386 for i := uint64(len(ih.IndexValues) - 1); i >= ih.IndexValuesNumber; i-- { 387 ih.IndexValues[i] = [32]byte{} 388 } 389 390 err = persiaos.StorageSetRecord(ih.ToSyllabFull()) 391 return 392 } 393 394 /* 395 -- Syllab Encoder & Decoder -- 396 */ 397 398 func (ih *IndexHash) FromSyllab(payload []byte, stackIndex uint32) { 399 if uint32(len(buf)) < ih.LenOfSyllabStack() { 400 err = syllab.ErrShortArrayDecode 401 return 402 } 403 404 copy(ih.RecordID[:], buf[0:]) 405 ih.RecordStructureID = syllab.GetUInt64(buf, 32) 406 ih.RecordSize = syllab.GetUInt64(buf, 40) 407 ih.WriteTime = time.Time(syllab.GetInt64(buf, 48)) 408 copy(ih.OwnerAppID[:], buf[56:]) 409 410 if ih.RecordStructureID != indexHashStructureID { 411 err = ErrRecordNotValid 412 return 413 } 414 415 ih.EarlierExpandTime = time.Time(syllab.GetInt64(buf, 88)) 416 ih.LastExpandTime = time.Time(syllab.GetInt64(buf, 96)) 417 ih.IndexValuesNumber = syllab.GetUInt64(buf, 104) 418 ih.IndexValuesCapacity = syllab.GetUInt64(buf, 112) 419 // Break syllab rules and don't get IndexValues Add&&len 420 buf = buf[ih.LenOfSyllabStack():] 421 ih.IndexValues = convert.UnsafeByteSliceTo32ByteArraySlice(buf) 422 return 423 } 424 425 func (ih *IndexHash) ToSyllabFull() (buf []byte) { 426 buf = make([]byte, uint64(ih.LenAsSyllab())) 427 copy(buf[ih.LenOfSyllabStack():], convert.Unsafe32ByteArraySliceToByteSlice(ih.IndexValues)) 428 ih.ToSyllabHeader(buf) 429 return 430 } 431 432 func (ih *IndexHash) ToSyllabHeader(buf []byte) { 433 copy(buf[0:], ih.RecordID[:]) 434 syllab.SetUInt64(buf, 32, indexHashStructureID) 435 syllab.SetUInt64(buf, 40, ih.RecordSize) 436 syllab.SetInt64(buf, 48, int64(ih.WriteTime)) 437 copy(buf[56:], ih.OwnerAppID[:]) 438 439 syllab.SetInt64(buf, 88, int64(ih.EarlierExpandTime)) 440 syllab.SetInt64(buf, 96, int64(ih.LastExpandTime)) 441 syllab.SetUInt64(buf, 104, ih.IndexValuesNumber) 442 syllab.SetUInt64(buf, 112, ih.IndexValuesCapacity) 443 // Break syllab rules and don't set IndexValues Add&&len 444 } 445 446 func (ih *IndexHash) LenOfSyllabStack() uint32 { 447 return 120 // 88 + 8 + 8 + 8 + 8 + ? !!don't need IndexValues Add&&len!! 448 } 449 450 func (ih *IndexHash) LenOfSyllabHeap() (ln uint32) { 451 ln += uint32(ih.IndexValuesCapacity * 32) 452 return 453 } 454 455 func (ih *IndexHash) LenAsSyllab() uint64 { 456 return uint64(ih.LenOfSyllabStack() + ih.LenOfSyllabHeap()) 457 }