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  }