or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli.mdcontexts.mdfile-operations.mdfilesystem.mdindex.mdlow-level-api.md

contexts.mddocs/

0

# Context Classes

1

2

Storage backend implementations that handle block-level read, program, erase, and sync operations for different storage targets. Context classes provide the abstraction layer between LittleFS and physical storage, enabling support for memory buffers, disk devices, and custom storage implementations.

3

4

## Capabilities

5

6

### UserContext Class

7

8

Basic memory-based context implementation that stores filesystem data in a Python bytearray. This is the default context used for most applications and provides a simple in-memory filesystem suitable for creating filesystem images.

9

10

```python { .api }

11

class UserContext:

12

def __init__(self, buffsize: int) -> None:

13

"""

14

Initialize memory-based storage context.

15

16

Parameters:

17

- buffsize: int, size of memory buffer in bytes

18

19

The buffer is initialized with 0xFF bytes (erased flash state).

20

"""

21

22

def read(self, cfg: LFSConfig, block: int, off: int, size: int) -> bytearray:

23

"""

24

Read data from storage.

25

26

Parameters:

27

- cfg: LFSConfig, filesystem configuration

28

- block: int, block number to read from

29

- off: int, offset within block

30

- size: int, number of bytes to read

31

32

Returns:

33

bytearray: Data read from storage

34

"""

35

36

def prog(self, cfg: LFSConfig, block: int, off: int, data: bytes) -> int:

37

"""

38

Program (write) data to storage.

39

40

Parameters:

41

- cfg: LFSConfig, filesystem configuration

42

- block: int, block number to write to

43

- off: int, offset within block

44

- data: bytes, data to write

45

46

Returns:

47

int: 0 on success

48

"""

49

50

def erase(self, cfg: LFSConfig, block: int) -> int:

51

"""

52

Erase a block (set all bytes to 0xFF).

53

54

Parameters:

55

- cfg: LFSConfig, filesystem configuration

56

- block: int, block number to erase

57

58

Returns:

59

int: 0 on success

60

"""

61

62

def sync(self, cfg: LFSConfig) -> int:

63

"""

64

Sync cached data to storage.

65

66

Parameters:

67

- cfg: LFSConfig, filesystem configuration

68

69

Returns:

70

int: 0 on success

71

72

For memory context, this is a no-op since data is immediately available.

73

"""

74

75

@property

76

def buffer(self) -> bytearray:

77

"""Access to the underlying memory buffer."""

78

```

79

80

### UserContextWinDisk Class

81

82

Windows disk-based context implementation that provides direct access to Windows disk devices. This context enables LittleFS operations on physical drives, partitions, or disk image files using Windows Win32 APIs.

83

84

```python { .api }

85

class UserContextWinDisk(UserContext):

86

def __init__(self, disk_path: str) -> None:

87

"""

88

Initialize Windows disk-based storage context.

89

90

Parameters:

91

- disk_path: str, Windows device path (e.g., '\\\\.\\PhysicalDrive0', '\\\\.\\C:', 'image.bin')

92

93

Requires:

94

- Windows platform

95

- pywin32 package installed

96

- Appropriate permissions for disk access

97

98

Raises:

99

ImportError: If pywin32 is not available

100

IOError: If disk cannot be opened

101

"""

102

103

def read(self, cfg: LFSConfig, block: int, off: int, size: int) -> bytearray:

104

"""

105

Read data from Windows disk device.

106

107

Uses Win32 SetFilePointer and ReadFile APIs for direct disk access.

108

"""

109

110

def prog(self, cfg: LFSConfig, block: int, off: int, data: bytes) -> int:

111

"""

112

Program data to Windows disk device.

113

114

Uses Win32 SetFilePointer and WriteFile APIs for direct disk access.

115

"""

116

117

def erase(self, cfg: LFSConfig, block: int) -> int:

118

"""

119

Erase block on Windows disk device by writing 0xFF bytes.

120

121

Uses Win32 SetFilePointer and WriteFile APIs.

122

"""

123

124

def sync(self, cfg: LFSConfig) -> int:

125

"""

126

Sync data to Windows disk device.

127

128

Uses Win32 FlushFileBuffers API to ensure data is written to storage.

129

"""

130

131

def __del__(self):

132

"""Cleanup Windows handle on destruction."""

133

```

134

135

## Context Integration

136

137

Context objects are integrated with LFSConfig and automatically used by filesystem operations:

138

139

```python { .api }

140

class LFSConfig:

141

@property

142

def user_context(self) -> UserContext:

143

"""Access to the associated user context."""

144

145

# Context is automatically used for all filesystem operations

146

# through callback functions registered with the C library

147

```

148

149

## Usage Examples

150

151

### Memory Context Usage

152

153

```python

154

from littlefs import LittleFS, UserContext

155

156

# Create custom-sized memory context

157

context = UserContext(buffsize=2*1024*1024) # 2MB buffer

158

159

# Use with LittleFS

160

fs = LittleFS(context=context, block_size=4096, block_count=512)

161

162

# Perform filesystem operations

163

with fs.open('test.txt', 'w') as f:

164

f.write('Hello, World!')

165

166

# Access raw buffer data

167

raw_data = context.buffer

168

print(f"Buffer size: {len(raw_data)} bytes")

169

170

# Export filesystem image

171

with open('filesystem.bin', 'wb') as f:

172

f.write(raw_data)

173

```

174

175

### Windows Disk Context Usage

176

177

```python

178

from littlefs import LittleFS, UserContextWinDisk

179

180

# Access physical drive (requires admin privileges)

181

try:

182

disk_context = UserContextWinDisk('\\\\.\\PhysicalDrive1')

183

fs = LittleFS(context=disk_context, block_size=4096, mount=False)

184

185

# Format the drive with LittleFS

186

fs.format()

187

fs.mount()

188

189

# Use filesystem normally

190

fs.mkdir('data')

191

with fs.open('data/readme.txt', 'w') as f:

192

f.write('LittleFS on physical drive!')

193

194

except ImportError:

195

print("pywin32 not available")

196

except IOError as e:

197

print(f"Cannot access disk: {e}")

198

```

199

200

### Disk Image File Context

201

202

```python

203

# Create disk image file first

204

with open('disk_image.bin', 'wb') as f:

205

f.write(b'\\xff' * (1024 * 1024)) # 1MB blank image

206

207

# Use Windows context with file path

208

disk_context = UserContextWinDisk('disk_image.bin')

209

fs = LittleFS(context=disk_context, block_size=512, block_count=2048)

210

211

# Use filesystem

212

with fs.open('config.json', 'w') as f:

213

f.write('{"version": "1.0"}')

214

215

# Changes are written directly to disk_image.bin

216

fs.unmount()

217

```

218

219

### Custom Context Implementation

220

221

```python

222

import logging

223

224

class LoggingContext(UserContext):

225

"""Context that logs all operations for debugging."""

226

227

def __init__(self, buffsize: int):

228

super().__init__(buffsize)

229

self.logger = logging.getLogger(__name__)

230

231

def read(self, cfg, block, off, size):

232

self.logger.debug(f"READ: block={block}, off={off}, size={size}")

233

return super().read(cfg, block, off, size)

234

235

def prog(self, cfg, block, off, data):

236

self.logger.debug(f"PROG: block={block}, off={off}, size={len(data)}")

237

return super().prog(cfg, block, off, data)

238

239

def erase(self, cfg, block):

240

self.logger.debug(f"ERASE: block={block}")

241

return super().erase(cfg, block)

242

243

def sync(self, cfg):

244

self.logger.debug("SYNC")

245

return super().sync(cfg)

246

247

# Use custom context

248

logging.basicConfig(level=logging.DEBUG)

249

ctx = LoggingContext(64*1024) # 64KB with logging

250

fs = LittleFS(context=ctx, block_size=512, block_count=128)

251

252

# All operations will be logged

253

with fs.open('debug.txt', 'w') as f:

254

f.write('This will generate read/prog/erase logs')

255

```

256

257

### Context Buffer Analysis

258

259

```python

260

# Analyze raw filesystem data

261

fs = LittleFS(block_size=512, block_count=256)

262

263

# Create some files

264

with fs.open('file1.txt', 'w') as f:

265

f.write('Content of file 1')

266

267

with fs.open('file2.txt', 'w') as f:

268

f.write('Content of file 2')

269

270

# Examine raw buffer

271

buffer = fs.context.buffer

272

print(f"Total buffer size: {len(buffer)} bytes")

273

274

# Find used vs unused space

275

used_blocks = fs.used_block_count

276

total_blocks = fs.block_count

277

block_size = 512

278

279

print(f"Used blocks: {used_blocks}/{total_blocks}")

280

print(f"Used space: {used_blocks * block_size} bytes")

281

print(f"Free space: {(total_blocks - used_blocks) * block_size} bytes")

282

283

# Look for filesystem signatures (first few bytes often contain metadata)

284

header = buffer[:64]

285

print(f"Filesystem header: {header[:16].hex()}")

286

287

# Find erased regions (0xFF bytes indicate unused flash)

288

erased_start = None

289

for i, byte in enumerate(buffer):

290

if byte == 0xff:

291

if erased_start is None:

292

erased_start = i

293

else:

294

if erased_start is not None:

295

size = i - erased_start

296

if size > 512: # Only report significant erased regions

297

print(f"Erased region: {erased_start}-{i} ({size} bytes)")

298

erased_start = None

299

```

300

301

### Performance Context

302

303

```python

304

import time

305

306

class PerformanceContext(UserContext):

307

"""Context that measures performance metrics."""

308

309

def __init__(self, buffsize: int):

310

super().__init__(buffsize)

311

self.read_count = 0

312

self.write_count = 0

313

self.erase_count = 0

314

self.read_bytes = 0

315

self.write_bytes = 0

316

self.read_time = 0.0

317

self.write_time = 0.0

318

319

def read(self, cfg, block, off, size):

320

start = time.time()

321

result = super().read(cfg, block, off, size)

322

self.read_time += time.time() - start

323

self.read_count += 1

324

self.read_bytes += size

325

return result

326

327

def prog(self, cfg, block, off, data):

328

start = time.time()

329

result = super().prog(cfg, block, off, data)

330

self.write_time += time.time() - start

331

self.write_count += 1

332

self.write_bytes += len(data)

333

return result

334

335

def erase(self, cfg, block):

336

self.erase_count += 1

337

return super().erase(cfg, block)

338

339

def get_stats(self):

340

return {

341

'reads': self.read_count,

342

'writes': self.write_count,

343

'erases': self.erase_count,

344

'read_bytes': self.read_bytes,

345

'write_bytes': self.write_bytes,

346

'read_time': self.read_time,

347

'write_time': self.write_time,

348

'read_throughput': self.read_bytes / self.read_time if self.read_time > 0 else 0,

349

'write_throughput': self.write_bytes / self.write_time if self.write_time > 0 else 0

350

}

351

352

# Measure filesystem performance

353

perf_ctx = PerformanceContext(512*1024) # 512KB

354

fs = LittleFS(context=perf_ctx, block_size=4096, block_count=128)

355

356

# Perform operations

357

start_time = time.time()

358

359

for i in range(100):

360

with fs.open(f'file_{i:03d}.txt', 'w') as f:

361

f.write(f'Content for file {i}\\n' * 10)

362

363

total_time = time.time() - start_time

364

365

# Report performance metrics

366

stats = perf_ctx.get_stats()

367

print(f"Total time: {total_time:.3f}s")

368

print(f"Operations: {stats['reads']} reads, {stats['writes']} writes, {stats['erases']} erases")

369

print(f"Data: {stats['read_bytes']} bytes read, {stats['write_bytes']} bytes written")

370

print(f"Throughput: {stats['read_throughput']:.0f} B/s read, {stats['write_throughput']:.0f} B/s write")

371

print(f"Average: {100/total_time:.1f} files/second")

372

```