Skip to content

Commit c2818af

Browse files
upy-fs-builder: Let getIntelHexFiles() & addIntelHexFiles() take a MemoryMap.
Input argument can now be an Intel Hex string or a MemoryMap. This helps increase time to takes to construct MicropythonFsHex instances.
1 parent f218836 commit c2818af

File tree

4 files changed

+71
-40
lines changed

4 files changed

+71
-40
lines changed

src/__tests__/micropython-fs-builder.spec.ts

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
} from '../micropython-fs-builder';
2323

2424
const uPyHexFile = fs.readFileSync('./src/__tests__/upy-v1.0.1.hex', 'utf8');
25+
const uPyHexMap = MemoryMap.fromHex(uPyHexFile);
2526
const makecodeHexFile = fs.readFileSync('./src/__tests__/makecode.hex', 'utf8');
2627
const randContent = strToBytes('Some random content.');
2728

@@ -209,6 +210,21 @@ describe('Writing files to the filesystem.', () => {
209210
expect(file1data).toEqual(files[1].bytes());
210211
});
211212

213+
it('Input to addIntelHexFiles() as a Intel Hex string and Memory Map generate compatible data', () => {
214+
const fsFiles = {
215+
[files[0].fileName]: strToBytes(files[0].fileStr),
216+
[files[1].fileName]: strToBytes(files[1].fileStr),
217+
};
218+
for (let i = 0; i < 32; i++) {
219+
fsFiles['file_' + i + '.txt'] = strToBytes("Content doesn't matter " + i);
220+
}
221+
222+
const hexFromIntelHex = addIntelHexFiles(uPyHexFile, fsFiles) as string;
223+
const hexFromMemoryMap = addIntelHexFiles(uPyHexMap, fsFiles) as string;
224+
225+
expect(hexFromIntelHex).toEqual(hexFromMemoryMap);
226+
});
227+
212228
it('Both addIntelHexFiles() and generateHexWithFiles() generate compatible data', () => {
213229
const fsFiles = {
214230
[files[0].fileName]: strToBytes(files[0].fileStr),
@@ -315,7 +331,7 @@ describe('Writing files to the filesystem.', () => {
315331
};
316332

317333
it('Can generate a full chunk that also consumes the next one.', () => {
318-
const fwWithFsOther = addIntelHexFiles(uPyHexFile, {
334+
const fwWithFsOther = addIntelHexFiles(uPyHexMap, {
319335
[fullChunkPlus.fileName]: strToBytes(fullChunkPlus.fileStr),
320336
});
321337

@@ -327,7 +343,7 @@ describe('Writing files to the filesystem.', () => {
327343
});
328344

329345
it('Correctly generate an almost full chunk (not using last byte).', () => {
330-
const fwWithFsOther = addIntelHexFiles(uPyHexFile, {
346+
const fwWithFsOther = addIntelHexFiles(uPyHexMap, {
331347
[fullChunkMinus.fileName]: strToBytes(fullChunkMinus.fileStr),
332348
});
333349

@@ -339,7 +355,7 @@ describe('Writing files to the filesystem.', () => {
339355
});
340356

341357
it('Correctly generate just over a full chunk.', () => {
342-
const fwWithFsOther = addIntelHexFiles(uPyHexFile, {
358+
const fwWithFsOther = addIntelHexFiles(uPyHexMap, {
343359
[twoChunks.fileName]: strToBytes(twoChunks.fileStr),
344360
});
345361

@@ -351,21 +367,21 @@ describe('Writing files to the filesystem.', () => {
351367
});
352368

353369
it('Empty file name throws an error.', () => {
354-
const failCase = () => addIntelHexFiles(uPyHexFile, { '': randContent });
370+
const failCase = () => addIntelHexFiles(uPyHexMap, { '': randContent });
355371

356372
expect(failCase).toThrow('File has to have a file name');
357373
});
358374

359375
it('Empty file data throw an error.', () => {
360376
const failCase = () =>
361-
addIntelHexFiles(uPyHexFile, { 'my_file.txt': new Uint8Array(0) });
377+
addIntelHexFiles(uPyHexMap, { 'my_file.txt': new Uint8Array(0) });
362378

363379
expect(failCase).toThrow('has to contain data');
364380
});
365381

366382
it('Large file that does not fit throws error.', () => {
367383
const failCase = () => {
368-
addIntelHexFiles(uPyHexFile, {
384+
addIntelHexFiles(uPyHexMap, {
369385
'my_file.txt': new Uint8Array(50 * 1024).fill(0x55),
370386
});
371387
};
@@ -402,15 +418,14 @@ describe('Writing files to the filesystem.', () => {
402418

403419
it('Add a group of files that do not fit.', () => {
404420
// The MicroPython hex has about 29 KBs
405-
const hexWithFs = uPyHexFile;
406421
// Use 4 KB blocks per file (each chunk is 128 B)
407422
const fakeBigFileData = new Uint8Array(4000).fill(0x55);
408423
const tooManyBigFiles: { [filename: string]: Uint8Array } = {};
409424
for (let i = 0; i < 8; i++) {
410425
tooManyBigFiles['file_' + i + '.txt'] = fakeBigFileData;
411426
}
412427

413-
const addingAllFiles = () => addIntelHexFiles(uPyHexFile, tooManyBigFiles);
428+
const addingAllFiles = () => addIntelHexFiles(uPyHexMap, tooManyBigFiles);
414429

415430
expect(addingAllFiles).toThrow('Not enough space');
416431
});
@@ -420,7 +435,7 @@ describe('Writing files to the filesystem.', () => {
420435
const largeName = 'a'.repeat(maxLength);
421436

422437
const workingCase = () =>
423-
addIntelHexFiles(uPyHexFile, { [largeName]: randContent });
438+
addIntelHexFiles(uPyHexMap, { [largeName]: randContent });
424439

425440
expect(workingCase).not.toThrow(Error);
426441
});
@@ -430,7 +445,7 @@ describe('Writing files to the filesystem.', () => {
430445
const largeName = 'a'.repeat(maxLength + 1);
431446

432447
const failCase = () =>
433-
addIntelHexFiles(uPyHexFile, { [largeName]: randContent });
448+
addIntelHexFiles(uPyHexMap, { [largeName]: randContent });
434449

435450
expect(failCase).toThrow('File name');
436451
});
@@ -618,16 +633,18 @@ describe('Reading files from the filesystem.', () => {
618633
hexMap.set(index, value);
619634
});
620635
};
621-
const fullUpyFsMemMap = MemoryMap.fromHex(uPyHexFile);
636+
const fullUpyFsMemMap = uPyHexMap.clone();
622637
addHexToMap(fullUpyFsMemMap, afirstHex);
623638
addHexToMap(fullUpyFsMemMap, alastHex);
624639
addHexToMap(fullUpyFsMemMap, mainHex);
625640

626-
const foundFiles = getIntelHexFiles(fullUpyFsMemMap.asHexString());
641+
const foundFilesFromHex = getIntelHexFiles(fullUpyFsMemMap.asHexString());
642+
const foundFilesFromMap = getIntelHexFiles(fullUpyFsMemMap);
627643

628-
expect(foundFiles).toHaveProperty([afirstFilename], afirstContent);
629-
expect(foundFiles).toHaveProperty([alastFilename], alastContent);
630-
expect(foundFiles).toHaveProperty([mainFilename], mainContent);
644+
expect(foundFilesFromHex).toEqual(foundFilesFromMap);
645+
expect(foundFilesFromHex).toHaveProperty([afirstFilename], afirstContent);
646+
expect(foundFilesFromHex).toHaveProperty([alastFilename], alastContent);
647+
expect(foundFilesFromHex).toHaveProperty([mainFilename], mainContent);
631648
});
632649

633650
// When MicroPython saves a file that takes full chunk it still utilises
@@ -656,15 +673,17 @@ describe('Reading files from the filesystem.', () => {
656673
// In the one_chunk_plus.py example the data inside the file would take
657674
// exactly 128 Bytes, or one chunk. However, MicroPython also "takes" or
658675
// "links" the next chunk and doesn't put any data into it.
659-
const fullUpyFsMemMap = MemoryMap.fromHex(uPyHexFile);
676+
const fullUpyFsMemMap = uPyHexMap.clone();
660677
const oneChunkPlusMemMap = MemoryMap.fromHex(oneChunkPlusHex);
661678
oneChunkPlusMemMap.forEach((value: Uint8Array, index: number) => {
662679
fullUpyFsMemMap.set(index, value);
663680
});
664681

665-
const foundFiles = getIntelHexFiles(fullUpyFsMemMap.asHexString());
682+
const foundFilesFromHex = getIntelHexFiles(fullUpyFsMemMap.asHexString());
683+
const foundFilesFromMap = getIntelHexFiles(fullUpyFsMemMap);
666684

667-
expect(foundFiles).toHaveProperty(
685+
expect(foundFilesFromHex).toEqual(foundFilesFromMap);
686+
expect(foundFilesFromHex).toHaveProperty(
668687
[oneChunkPlusFilename],
669688
strToBytes(oneChunkPlusContent)
670689
);
@@ -692,15 +711,17 @@ describe('Reading files from the filesystem.', () => {
692711
it('Can read a file that occupies almost a full chunk.', () => {
693712
// In contrast to the one_chunk_plus.py example, this one fills the chunk
694713
// minus 1 Byte (The second 0xFF at the end is the chunk tail).
695-
const fullUpyFsMemMap = MemoryMap.fromHex(uPyHexFile);
714+
const fullUpyFsMemMap = uPyHexMap.clone();
696715
const oneChunkMinusMemMap = MemoryMap.fromHex(oneChunkMinusHex);
697716
oneChunkMinusMemMap.forEach((value: Uint8Array, index: number) => {
698717
fullUpyFsMemMap.set(index, value);
699718
});
700719

701-
const foundFiles = getIntelHexFiles(fullUpyFsMemMap.asHexString());
720+
const foundFilesFromHex = getIntelHexFiles(fullUpyFsMemMap.asHexString());
721+
const foundFilesFromMap = getIntelHexFiles(fullUpyFsMemMap);
702722

703-
expect(foundFiles).toHaveProperty(
723+
expect(foundFilesFromHex).toEqual(foundFilesFromMap);
724+
expect(foundFilesFromHex).toHaveProperty(
704725
[oneChunkMinusFilename],
705726
strToBytes(oneChunkMinusContent)
706727
);
@@ -722,7 +743,7 @@ describe('Reading files from the filesystem.', () => {
722743
it('Duplicate file names throws an error.', () => {
723744
// In contrast to the one_chunk_plus.py example, this one fills the chunk
724745
// minus 1 Byte (The second 0xFF at the end is the chunk tail).
725-
const fullUpyFsMemMap = MemoryMap.fromHex(uPyHexFile);
746+
const fullUpyFsMemMap = uPyHexMap.clone();
726747
const oneFileCopyMemMap = MemoryMap.fromHex(oneFileCopyHex);
727748
oneFileCopyMemMap.forEach((value: Uint8Array, index: number) => {
728749
fullUpyFsMemMap.set(index, value);
@@ -738,7 +759,7 @@ describe('Reading files from the filesystem.', () => {
738759
});
739760

740761
it('Reading files from empty MicroPython hex returns empty list.', () => {
741-
const foundFiles = getIntelHexFiles(uPyHexFile);
762+
const foundFiles = getIntelHexFiles(uPyHexMap);
742763

743764
expect(foundFiles).toEqual({});
744765
});
@@ -757,7 +778,7 @@ describe('Reading files from the filesystem.', () => {
757778

758779
describe('Calculate sizes.', () => {
759780
it('Get how much available fs space there is in a MicroPython hex file.', () => {
760-
const totalSize = getMemMapFsSize(MemoryMap.fromHex(uPyHexFile));
781+
const totalSize = getMemMapFsSize(uPyHexMap);
761782

762783
// Calculated by hand from the uPyHexFile v1.0.1 release.
763784
expect(totalSize).toEqual(27 * 1024);

src/__tests__/micropython-fs-hex.spec.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,6 @@ describe('Test hex generation.', () => {
475475
const returnedIntelHex = microbitFs.getIntelHexBytes();
476476

477477
expect(addIntelHexFilesSpy.mock.calls.length).toEqual(1);
478-
expect(addIntelHexFilesSpy.mock.calls[0][0]).toBe(uPyHexFile);
479478
expect(addIntelHexFilesSpy.mock.calls[0][2]).toBeTruthy();
480479
});
481480
});
@@ -583,10 +582,12 @@ describe('Test importing files from hex.', () => {
583582
expect(generateHexWithFilesSpy.mock.calls.length).toEqual(1);
584583
});
585584

586-
it('Constructor hex file with files to import thorws an error.', () => {
585+
it('Constructor hex file with files to import throws an error.', () => {
587586
const failCase = () => new MicropythonFsHex(hexStrWithFiles);
588587

589-
expect(failCase).toThrow(Error);
588+
expect(failCase).toThrow(
589+
'There are files in the MicropythonFsHex constructor hex file input'
590+
);
590591
});
591592

592593
it('Enabling formatFirst flag erases the previous files.', () => {

src/micropython-fs-builder.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -392,17 +392,22 @@ function addMemMapFile(
392392
* @throws {Error} When there are issues calculating the file system boundaries.
393393
* @throws {Error} When there is no space left for a file.
394394
*
395-
* @param intelHex - MicroPython Intel Hex string.
395+
* @param intelHex - MicroPython Intel Hex string or MemoryMap.
396396
* @param files - Hash table with filenames as the key and byte arrays as the
397397
* value.
398398
* @returns MicroPython Intel Hex string with the files in the filesystem.
399399
*/
400400
function addIntelHexFiles(
401-
intelHex: string,
401+
intelHex: string | MemoryMap,
402402
files: { [filename: string]: Uint8Array },
403403
returnBytes: boolean = false
404404
): string | Uint8Array {
405-
const intelHexMap: MemoryMap = MemoryMap.fromHex(intelHex);
405+
let intelHexMap: MemoryMap;
406+
if (typeof intelHex === 'string') {
407+
intelHexMap = MemoryMap.fromHex(intelHex);
408+
} else {
409+
intelHexMap = intelHex.clone();
410+
}
406411
Object.keys(files).forEach((filename) => {
407412
addMemMapFile(intelHexMap, filename, files[filename]);
408413
});
@@ -438,21 +443,26 @@ function generateHexWithFiles(
438443
}
439444

440445
/**
441-
* Reads the filesystem included in a MicroPython Intel Hex string.
446+
* Reads the filesystem included in a MicroPython Intel Hex string or Map.
442447
*
443448
* @throws {Error} When multiple files with the same name encountered.
444449
* @throws {Error} When a file chunk points to an unused chunk.
445450
* @throws {Error} When a file chunk marker does not point to previous chunk.
446451
* @throws {Error} When following through the chunks linked list iterates
447452
* through more chunks and used chunks (sign of an infinite loop).
448453
*
449-
* @param intelHex - The MicroPython Intel Hex string to read from.
454+
* @param intelHex - The MicroPython Intel Hex string or MemoryMap to read from.
450455
* @returns Dictionary with the filename as key and byte array as values.
451456
*/
452457
function getIntelHexFiles(
453-
intelHex: string
458+
intelHex: string | MemoryMap
454459
): { [filename: string]: Uint8Array } {
455-
const hexMap: MemoryMap = MemoryMap.fromHex(intelHex);
460+
let hexMap: MemoryMap;
461+
if (typeof intelHex === 'string') {
462+
hexMap = MemoryMap.fromHex(intelHex);
463+
} else {
464+
hexMap = intelHex.clone();
465+
}
456466
const startAddress: number = getStartAddress(hexMap);
457467
const endAddress: number = getLastPageAddress(hexMap);
458468

src/micropython-fs-hex.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ export class MicropythonFsHex implements FsInterface {
2424
* File System manager constructor.
2525
* At the moment it needs a MicroPython hex string without a files included.
2626
*
27-
* TODO: If files are already in input hex file, deal with them somehow.
28-
*
2927
* @param intelHex - MicroPython Intel Hex string.
3028
*/
3129
constructor(
@@ -36,13 +34,14 @@ export class MicropythonFsHex implements FsInterface {
3634
throw new Error('Invalid MicroPython hex invalid.');
3735
}
3836
this._uPyFsBuilderCache = createMpFsBuilderCache(intelHex);
39-
this.importFilesFromIntelHex(this._uPyFsBuilderCache.originalIntelHex);
40-
if (this.ls().length) {
37+
this.setStorageSize(maxFsSize || this._uPyFsBuilderCache.fsSize);
38+
// Check if there are files in the input hex
39+
const hexFiles = getIntelHexFiles(this._uPyFsBuilderCache.originalMemMap);
40+
if (Object.keys(hexFiles).length) {
4141
throw new Error(
4242
'There are files in the MicropythonFsHex constructor hex file input.'
4343
);
4444
}
45-
this.setStorageSize(maxFsSize || this._uPyFsBuilderCache.fsSize);
4645
}
4746

4847
/**
@@ -312,7 +311,7 @@ export class MicropythonFsHex implements FsInterface {
312311
files[file.filename] = file.getBytes();
313312
});
314313
return addIntelHexFiles(
315-
this._uPyFsBuilderCache.originalIntelHex,
314+
this._uPyFsBuilderCache.originalMemMap,
316315
files,
317316
true
318317
) as Uint8Array;

0 commit comments

Comments
 (0)