github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/cmn/jsp/file.go (about) 1 // Package jsp (JSON persistence) provides utilities to store and load arbitrary 2 // JSON-encoded structures with optional checksumming and compression. 3 /* 4 * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved. 5 */ 6 package jsp 7 8 import ( 9 "errors" 10 "fmt" 11 "os" 12 "strings" 13 14 "github.com/NVIDIA/aistore/cmn/cos" 15 "github.com/NVIDIA/aistore/cmn/debug" 16 "github.com/NVIDIA/aistore/cmn/nlog" 17 ) 18 19 const ( 20 signature = "aistore" // file signature 21 // 0 ---------------- 63 64 ------ 95 | 96 ------ 127 22 prefLen = 2 * cos.SizeofI64 // [ signature | jsp ver | meta version | bit flags ] 23 ) 24 25 // current JSP version 26 const ( 27 Metaver = 3 28 ) 29 30 ////////////////// 31 // main methods // 32 ////////////////// 33 34 func SaveMeta(filepath string, meta Opts, wto cos.WriterTo2) error { 35 return Save(filepath, meta, meta.JspOpts(), wto) 36 } 37 38 func Save(filepath string, v any, opts Options, wto cos.WriterTo2) (err error) { 39 var ( 40 file *os.File 41 tmp = filepath + ".tmp." + cos.GenTie() 42 ) 43 if file, err = cos.CreateFile(tmp); err != nil { 44 return 45 } 46 defer func() { 47 if err == nil { 48 return 49 } 50 if nestedErr := cos.RemoveFile(tmp); nestedErr != nil { 51 nlog.Errorf("Nested (%v): failed to remove %s, err: %v", err, tmp, nestedErr) 52 } 53 }() 54 if wto != nil { 55 err = wto.WriteTo2(file) 56 } else { 57 debug.Assert(v != nil) 58 err = Encode(file, v, opts) 59 } 60 if err != nil { 61 nlog.Errorf("Failed to encode %s: %v", filepath, err) 62 cos.Close(file) 63 return 64 } 65 if err = cos.FlushClose(file); err != nil { 66 nlog.Errorf("Failed to flush and close %s: %v", tmp, err) 67 return 68 } 69 err = os.Rename(tmp, filepath) 70 return 71 } 72 73 func LoadMeta(filepath string, meta Opts) (*cos.Cksum, error) { 74 return Load(filepath, meta, meta.JspOpts()) 75 } 76 77 func _tag(filepath string, v any) (tag string) { 78 tag = fmt.Sprintf("%T", v) 79 if i := strings.LastIndex(tag, "."); i > 0 { 80 tag = tag[i+1:] 81 } 82 return tag + " at " + filepath 83 } 84 85 func Load(filepath string, v any, opts Options) (checksum *cos.Cksum, err error) { 86 var file *os.File 87 file, err = os.Open(filepath) 88 if err != nil { 89 return 90 } 91 checksum, err = Decode(file, v, opts, _tag(filepath, v)) 92 if err == nil { 93 return 94 } 95 if errors.Is(err, &cos.ErrBadCksum{}) { 96 if errRm := os.Remove(filepath); errRm == nil { 97 cos.Errorf("jsp: %v, removed %q", err, filepath) 98 } else { 99 cos.Errorf("jsp: %v, failed to remove %q: %v", err, filepath, errRm) 100 } 101 } 102 return 103 }