github.com/blong14/gache@v0.0.0-20240124023949-89416fd8bbfa/internal/io/file/dat.go (about) 1 package file 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "errors" 7 "fmt" 8 "os" 9 "path" 10 "strings" 11 "sync" 12 ) 13 14 var pageSize int 15 16 func init() { 17 pageSize = os.Getpagesize() 18 } 19 20 func DatFileHeader(file string) []byte { 21 return []byte(fmt.Sprintf("begin 0755 %s\n", file)) 22 23 } 24 25 func DatFileFooter() []byte { 26 return []byte("\nend\n") 27 } 28 29 func NewDatFile(dir, fileName string) (*os.File, error) { 30 file := fmt.Sprintf("%s.dat", fileName) 31 p := path.Join(dir, file) 32 f, err := os.OpenFile(p, os.O_CREATE|os.O_RDWR, 0644) 33 if err != nil { 34 return nil, err 35 } 36 s, err := f.Stat() 37 if err != nil { 38 return nil, err 39 } 40 size := s.Size() 41 if size == 0 { 42 // memory ballast 43 buf := bytes.NewBuffer(nil) 44 buf.Write(DatFileHeader(file)) 45 buf.Write(make([]byte, pageSize*pageSize*4)) 46 buf.Write(DatFileFooter()) 47 _, err = f.Write(buf.Bytes()) 48 if err != nil { 49 return nil, err 50 } 51 } 52 return f, nil 53 } 54 55 const ( 56 DataStartIndex = 23 // file: begin 0755 default.dat\n<data>\nend\n 57 DataEndIndex = -3 58 ) 59 60 var ( 61 encoding = base64.StdEncoding.WithPadding(base64.NoPadding) 62 ) 63 64 type Decoded struct { 65 Data []byte 66 Filename string 67 Mode string 68 } 69 70 func Decode(data []byte) (*Decoded, error) { 71 dec := &Decoded{} 72 if len(data) < 2 { 73 return dec, errors.New("invalid decode input") 74 } 75 rows := strings.Split(string(data), "\n") 76 if strings.Split(rows[0], " ")[0] != "begin" { 77 return dec, errors.New("invalid format") 78 } 79 80 if strings.Split(rows[0], " ")[1] == " " || strings.Split(rows[0], " ")[1] == "" { 81 return dec, errors.New("invalid file permissions") 82 } 83 dec.Mode = strings.Split(rows[0], " ")[1] 84 85 if strings.Split(rows[0], " ")[2] == " " || strings.Split(rows[0], " ")[2] == "" { 86 return dec, errors.New("invalid filename") 87 } 88 dec.Filename = strings.Split(rows[0], " ")[2] 89 90 if rows[len(rows)-2] != "end" { 91 return dec, errors.New("invalid format: no 'end' marker found") 92 } 93 if rows[len(rows)-3] != "`" && rows[len(rows)-3] != " " { 94 return dec, errors.New("invalid ending format") 95 } 96 97 rows = rows[1 : len(rows)-3] 98 99 var err error 100 dec.Data, err = DecodeBlock(rows) 101 return dec, err 102 } 103 104 // DecodeBlock decodes a uuencoded text block 105 func DecodeBlock(rows []string) ([]byte, error) { 106 data := []byte{} 107 for i, row := range rows { 108 res, err := DecodeLine(row) 109 if err != nil { 110 return data, fmt.Errorf("DecodeBlock at line %d: %s", i+1, err) 111 } 112 data = append(data, res...) 113 } 114 return data, nil 115 } 116 117 // DecodeLine decodes a single line of uuencoded text 118 func DecodeLine(s string) ([]byte, error) { 119 if len(s) < 2 { 120 return nil, errors.New("invalid line input") 121 } 122 123 // fix up non-standard padding `, to make golang's base64 not freak out 124 s = strings.ReplaceAll(s, "`", " ") 125 126 // data := []byte(s) 127 // l := data[0] - 32 // length 128 res, err := encoding.DecodeString(s[1:]) 129 if err != nil { 130 return res, err 131 } 132 // if len(res) < int(l) { 133 // return nil, errors.New("line decoding failed") 134 // } 135 return res, nil 136 } 137 138 // Encode encodes data into uuencoded format, with header and footer 139 func Encode(data []byte, filename, mode string) ([]byte, error) { 140 out := []byte{} 141 out = append(out, fmt.Sprintf("begin %s %s\n", mode, filename)...) 142 143 enc, err := EncodeBlock(data) 144 if err != nil { 145 return nil, err 146 } 147 out = append(out, enc...) 148 149 out = append(out, "`\nend\n"...) 150 return out, nil 151 } 152 153 var pool = sync.Pool{New: func() interface{} { return bytes.NewBuffer(nil) }} 154 155 type Arena []byte 156 157 var mtx sync.Mutex 158 159 func (na *Arena) Get(len_ int) []byte { 160 mtx.Lock() 161 defer mtx.Unlock() 162 if len(*na) == 0 { 163 *na = make([]byte, 4096*4096*4) 164 } 165 offset := (len(*na) - 1) - len_ 166 if offset <= 0 { 167 *na = make([]byte, len(*na)+len_) 168 offset = (len(*na) - 1) - len_ 169 } 170 if offset == -2075 { 171 fmt.Println("hellow") 172 } 173 n := (*na)[offset : len(*na)-1] 174 *na = (*na)[:offset] 175 return n 176 } 177 178 var byteArena = make(Arena, 4096*4096*4) 179 180 // EncodeBlock encodes data in raw uuencoded format 181 func EncodeBlock(data []byte) ([]byte, error) { 182 out := byteArena.Get(base64.StdEncoding.EncodedLen(len(data)) + 2) 183 out[0] = byte(len(data)) 184 encoding.Encode(out[1:], data) 185 out[len(out)-1] = byte('\n') 186 return out, nil 187 }