1 package memfs 2 3 import ( 4 "io" 5 "io/fs" 6 "time" 7 "unicode/utf8" 8 ) 9 10 type opMode uint8 11 12 const ( 13 opClose opMode = 0 14 opRead opMode = 1 << iota 15 opWrite 16 opSeek 17 ) 18 19 type inode struct { 20 modtime time.Time 21 data []byte 22 mode fs.FileMode 23 } 24 25 func (i *inode) open(name string, mode opMode) (fs.File, error) { 26 if mode&opRead > 0 && i.mode&0o444 == 0 || mode&opWrite > 0 && i.mode&0o222 == 0 { 27 return nil, fs.ErrPermission 28 } 29 30 return &file{ 31 name: name, 32 inode: i, 33 opMode: mode, 34 }, nil 35 } 36 37 func (i *inode) bytes() ([]byte, error) { 38 if i.mode&0o444 == 0 { 39 return nil, fs.ErrPermission 40 } 41 42 return i.data, nil 43 } 44 45 func (i *inode) setMode(mode fs.FileMode) { 46 i.mode = i.mode&fs.ModeSymlink | mode 47 } 48 49 func (i *inode) setTimes(_, mtime time.Time) { 50 i.modtime = mtime 51 } 52 53 func (i *inode) seal() directoryEntry { 54 return i 55 } 56 57 func (i *inode) getEntry(_ string) (*dirEnt, error) { 58 return nil, fs.ErrInvalid 59 } 60 61 type file struct { 62 name string 63 *inode 64 opMode opMode 65 lastRead uint8 66 pos int64 67 } 68 69 func (f *file) validTo(op string, m opMode, needValidPos bool) error { 70 if f.opMode == opClose { 71 return &fs.PathError{ 72 Op: op, 73 Path: f.name, 74 Err: fs.ErrClosed, 75 } 76 } 77 78 if f.opMode&m != m { 79 return &fs.PathError{ 80 Op: op, 81 Path: f.name, 82 Err: fs.ErrInvalid, 83 } 84 } 85 86 if needValidPos && f.pos >= int64(len(f.data)) { 87 return io.EOF 88 } 89 90 return nil 91 } 92 93 func (f *file) Info() (fs.FileInfo, error) { 94 return f, nil 95 } 96 97 func (f *file) Stat() (fs.FileInfo, error) { 98 return f, nil 99 } 100 101 func (f *file) Read(p []byte) (int, error) { 102 if err := f.validTo("read", opRead, true); err != nil { 103 return 0, err 104 } 105 106 n := copy(p, f.data[f.pos:]) 107 108 f.pos += int64(n) 109 f.lastRead = 0 110 111 return n, nil 112 } 113 114 func (f *file) ReadAt(p []byte, off int64) (int, error) { 115 if err := f.validTo("readat", opRead|opSeek, false); err != nil { 116 return 0, err 117 } 118 119 if off >= int64(len(f.data)) { 120 return 0, io.EOF 121 } 122 123 n := copy(p, f.data[off:]) 124 125 if n < len(p) { 126 return n, io.EOF 127 } 128 129 return n, nil 130 } 131 132 func (f *file) ReadByte() (byte, error) { 133 if err := f.validTo("readbyte", opRead, true); err != nil { 134 return 0, err 135 } 136 137 b := f.data[f.pos] 138 139 f.pos++ 140 f.lastRead = 1 141 142 return b, nil 143 } 144 145 func (f *file) UnreadByte() error { 146 if err := f.validTo("unreadbyte", opRead|opSeek, false); err != nil { 147 return err 148 } 149 150 if f.lastRead != 1 { 151 return &fs.PathError{ 152 Op: "unreadbyte", 153 Path: f.name, 154 Err: fs.ErrInvalid, 155 } 156 } 157 158 f.lastRead = 0 159 f.pos-- 160 161 return nil 162 } 163 164 func (f *file) ReadRune() (rune, int, error) { 165 if err := f.validTo("readrune", opRead, true); err != nil { 166 return 0, 0, err 167 } 168 169 r, s := utf8.DecodeRune(f.data[f.pos:]) 170 171 f.lastRead = uint8(s) 172 f.pos += int64(s) 173 174 return r, s, nil 175 } 176 177 func (f *file) UnreadRune() error { 178 if err := f.validTo("unreadrune", opRead|opSeek, false); err != nil { 179 return err 180 } 181 182 if f.lastRead == 0 { 183 return &fs.PathError{ 184 Op: "unreadrune", 185 Path: f.name, 186 Err: fs.ErrInvalid, 187 } 188 } 189 190 f.pos -= int64(f.lastRead) 191 f.lastRead = 0 192 193 return nil 194 } 195 196 func (f *file) WriteTo(w io.Writer) (int64, error) { 197 if err := f.validTo("writeto", opRead, true); err != nil { 198 return 0, err 199 } 200 201 n, err := w.Write(f.data[f.pos:]) 202 f.pos += int64(n) 203 f.lastRead = 0 204 205 return int64(n), err 206 } 207 208 func (f *file) Seek(offset int64, whence int) (int64, error) { 209 if err := f.validTo("seek", opSeek, false); err != nil { 210 return 0, err 211 } 212 213 switch whence { 214 case io.SeekStart: 215 f.pos = offset 216 case io.SeekCurrent: 217 f.pos += offset 218 case io.SeekEnd: 219 f.pos = int64(len(f.data)) + offset 220 default: 221 return 0, &fs.PathError{ 222 Op: "seek", 223 Path: f.name, 224 Err: fs.ErrInvalid, 225 } 226 } 227 228 f.lastRead = 0 229 230 if f.pos < 0 { 231 f.pos = 0 232 233 return f.pos, &fs.PathError{ 234 Op: "seek", 235 Path: f.name, 236 Err: fs.ErrInvalid, 237 } 238 } 239 240 return f.pos, nil 241 } 242 243 func (f *file) Close() error { 244 err := f.validTo("close", opClose, false) 245 246 f.opMode = opClose 247 f.pos = 0 248 f.lastRead = 0 249 250 return err 251 } 252 253 func (f *file) Name() string { 254 return f.name 255 } 256 257 func (i *inode) Size() int64 { 258 return int64(len(i.data)) 259 } 260 261 func (i *inode) Type() fs.FileMode { 262 return i.mode.Type() 263 } 264 265 func (i *inode) Mode() fs.FileMode { 266 return i.mode 267 } 268 269 func (i *inode) ModTime() time.Time { 270 return i.modtime 271 } 272 273 func (i *inode) IsDir() bool { 274 return false 275 } 276 277 func (f *file) Sys() any { 278 return f 279 } 280