github.com/0chain/gosdk@v1.17.11/dev/blobber/model/ref.go (about) 1 package model 2 3 import ( 4 "context" 5 "math" 6 "reflect" 7 "strconv" 8 "strings" 9 10 "github.com/0chain/gosdk/core/encryption" 11 "github.com/0chain/gosdk/core/pathutil" 12 ) 13 14 const ( 15 // FileRef represents a file 16 FILE = "f" 17 18 // FileRef represents a directory 19 DIRECTORY = "d" 20 21 CHUNK_SIZE = 64 * 1024 22 23 DIR_LIST_TAG = "dirlist" 24 FILE_LIST_TAG = "filelist" 25 ) 26 27 type Ref struct { 28 Type string `gorm:"column:type" dirlist:"type" filelist:"type"` 29 AllocationID string `gorm:"column:allocation_id"` 30 LookupHash string `gorm:"column:lookup_hash" dirlist:"lookup_hash" filelist:"lookup_hash"` 31 Name string `gorm:"column:name" dirlist:"name" filelist:"name"` 32 Path string `gorm:"column:path" dirlist:"path" filelist:"path"` 33 Hash string `gorm:"column:hash" dirlist:"hash" filelist:"hash"` 34 NumBlocks int64 `gorm:"column:num_of_blocks" dirlist:"num_of_blocks" filelist:"num_of_blocks"` 35 PathHash string `gorm:"column:path_hash" dirlist:"path_hash" filelist:"path_hash"` 36 ParentPath string `gorm:"column:parent_path"` 37 PathLevel int `gorm:"column:level"` 38 ValidationRoot string `gorm:"column:validation_root" filelist:"validation_root"` 39 Size int64 `gorm:"column:size" dirlist:"size" filelist:"size"` 40 FixedMerkleRoot string `gorm:"column:fixed_merkle_root" filelist:"fixed_merkle_root"` 41 ActualFileSize int64 `gorm:"column:actual_file_size" filelist:"actual_file_size"` 42 ActualFileHash string `gorm:"column:actual_file_hash" filelist:"actual_file_hash"` 43 44 Children []*Ref `gorm:"-"` 45 childrenLoaded bool 46 47 ChunkSize int64 `gorm:"column:chunk_size" dirlist:"chunk_size" filelist:"chunk_size"` 48 } 49 50 func (r *Ref) CalculateHash(ctx context.Context) (string, error) { 51 if r.Type == DIRECTORY { 52 return r.CalculateDirHash(ctx) 53 } 54 return r.CalculateFileHash(ctx) 55 } 56 57 // GetListingData reflect and convert all fields into map[string]interface{} 58 func (r *Ref) GetListingData(ctx context.Context) map[string]interface{} { 59 if r == nil { 60 return make(map[string]interface{}) 61 } 62 63 if r.Type == FILE { 64 return GetListingFieldsMap(*r, FILE_LIST_TAG) 65 } 66 return GetListingFieldsMap(*r, DIR_LIST_TAG) 67 } 68 69 func GetListingFieldsMap(refEntity interface{}, tagName string) map[string]interface{} { 70 result := make(map[string]interface{}) 71 t := reflect.TypeOf(refEntity) 72 v := reflect.ValueOf(refEntity) 73 // Iterate over all available fields and read the tag value 74 for i := 0; i < t.NumField(); i++ { 75 field := t.Field(i) 76 77 // Get the field tag value 78 tag := field.Tag.Get(tagName) 79 // Skip if tag is not defined or ignored 80 if !field.Anonymous && (tag == "" || tag == "-") { 81 continue 82 } 83 84 if field.Anonymous { 85 listMap := GetListingFieldsMap(v.FieldByName(field.Name).Interface(), tagName) 86 if len(listMap) > 0 { 87 for k, v := range listMap { 88 result[k] = v 89 } 90 91 } 92 } else { 93 fieldValue := v.FieldByName(field.Name).Interface() 94 if fieldValue == nil { 95 continue 96 } 97 result[tag] = fieldValue 98 } 99 100 } 101 return result 102 } 103 104 func GetSubDirsFromPath(p string) []string { 105 path := p 106 parent, cur := pathutil.Split(path) 107 subDirs := make([]string, 0) 108 for len(cur) > 0 { 109 if cur == "." { 110 break 111 } 112 subDirs = append([]string{cur}, subDirs...) 113 parent, cur = pathutil.Split(parent) 114 } 115 return subDirs 116 } 117 118 func (r *Ref) CalculateDirHash(ctx context.Context) (string, error) { 119 // empty directory, return hash directly 120 if len(r.Children) == 0 && !r.childrenLoaded { 121 return r.Hash, nil 122 } 123 childHashes := make([]string, len(r.Children)) 124 childPathHashes := make([]string, len(r.Children)) 125 var refNumBlocks int64 126 var size int64 127 for index, childRef := range r.Children { 128 _, err := childRef.CalculateHash(ctx) 129 if err != nil { 130 return "", err 131 } 132 childHashes[index] = childRef.Hash 133 childPathHashes[index] = childRef.PathHash 134 refNumBlocks += childRef.NumBlocks 135 size += childRef.Size 136 } 137 138 r.Hash = encryption.Hash(strings.Join(childHashes, ":")) 139 r.NumBlocks = refNumBlocks 140 r.Size = size 141 r.PathHash = encryption.Hash(strings.Join(childPathHashes, ":")) 142 r.PathLevel = len(GetSubDirsFromPath(r.Path)) + 1 143 r.LookupHash = GetReferenceLookup(r.AllocationID, r.Path) 144 145 return r.Hash, nil 146 } 147 148 // GetReferenceLookup hash(allocationID + ":" + path) which is used to lookup the file reference in the db. 149 // - allocationID is the allocation ID. 150 // - path is the path of the file. 151 func GetReferenceLookup(allocationID string, path string) string { 152 return encryption.Hash(allocationID + ":" + path) 153 } 154 155 func (fr *Ref) CalculateFileHash(ctx context.Context) (string, error) { 156 fr.Hash = encryption.Hash(fr.GetFileHashData()) 157 fr.NumBlocks = int64(math.Ceil(float64(fr.Size*1.0) / float64(fr.ChunkSize))) 158 fr.PathHash = GetReferenceLookup(fr.AllocationID, fr.Path) 159 fr.PathLevel = len(GetSubDirsFromPath(fr.Path)) + 1 160 fr.LookupHash = GetReferenceLookup(fr.AllocationID, fr.Path) 161 162 return fr.Hash, nil 163 } 164 165 func (fr *Ref) GetFileHashData() string { 166 hashArray := make([]string, 0, 11) 167 hashArray = append(hashArray, fr.AllocationID) 168 hashArray = append(hashArray, fr.Type) 169 hashArray = append(hashArray, fr.Name) 170 hashArray = append(hashArray, fr.Path) 171 hashArray = append(hashArray, strconv.FormatInt(fr.Size, 10)) 172 hashArray = append(hashArray, fr.ValidationRoot) 173 hashArray = append(hashArray, fr.FixedMerkleRoot) 174 hashArray = append(hashArray, strconv.FormatInt(fr.ActualFileSize, 10)) 175 hashArray = append(hashArray, fr.ActualFileHash) 176 hashArray = append(hashArray, strconv.FormatInt(fr.ChunkSize, 10)) 177 178 return strings.Join(hashArray, ":") 179 }