simplify file API to simple read/write

This commit is contained in:
Gordon Williams 2014-05-14 14:35:19 +01:00
parent fd15bbf7e7
commit 3e88ad60d5
9 changed files with 131 additions and 183 deletions

View File

@ -15,19 +15,13 @@
*/
#include "jswrap_file.h"
#define JS_FS_DATA_NAME JS_HIDDEN_CHAR_STR"FSdata"
#define JS_FS_OPEN_FILES_NAME JS_HIDDEN_CHAR_STR"FSOpenFiles"
#define JS_FS_OPEN_PIPES_NAME JS_HIDDEN_CHAR_STR"FSOpenPipes"
#define JS_FS_DATA_NAME JS_HIDDEN_CHAR_STR"FSdata" // the data in each file
#define JS_FS_OPEN_FILES_NAME JS_HIDDEN_CHAR_STR"FSOpenFiles" // the list of open files
// from jswrap_fat
extern bool jsfsInit();
extern void jsfsReportError(const char *msg, FRESULT res);
//object methods handles
size_t _readFile(JsFile* file, JsVar* buffer, int length, int position, FRESULT* res);
size_t _writeFile(JsFile* file, JsVar* buffer, int length, int position, FRESULT* res);
void _closeFile(JsFile* file);
/*JSON{ "type":"library",
"class" : "File",
"description" : ["This is the stream related file IO library.",
@ -53,9 +47,6 @@ static bool fileGetFromVar(JsFile *file, JsVar *parent) {
jsvGetString(fHandle, (char*)&file->data, sizeof(JsFileData)+1/*trailing zero*/);
jsvUnLock(fHandle);
file->fileVar = parent;
file->read = _readFile;
file->write = _writeFile;
file->close = _closeFile;
if(file->data.state == FS_OPEN) {// return false if the file has been closed.
ret = true;
}
@ -104,9 +95,6 @@ static bool allocateJsFile(JsFile* file,FileMode mode, FileType type) {
file->data.mode = mode;
file->data.type = type;
file->data.state = FS_NONE;
file->read = _readFile;
file->write = _writeFile;
file->close = _closeFile;
ret = true;
}
return ret;
@ -171,12 +159,19 @@ JsVar *jswrap_file_constructor(JsVar* path, JsVar* mode) {
"generate_full" : "jswrap_file_close(parent)",
"description" : [ "Close an open file."]
}*/
//fs.close(fd)
void jswrap_file_close(JsVar* parent) {
if (jsfsInit()) {
JsFile file;
if (fileGetFromVar(&file, parent) && file.data.state == FS_OPEN) {
file.close(&file);
#ifndef LINUX
f_close(&file.data.handle);
#else
fclose(file.data.handle);
file.data.handle = 0;
#endif
file.data.state = FS_CLOSED;
fileSetVar(&file);
JsVar *arr = fsGetArray(JS_FS_OPEN_FILES_NAME, false);
if (arr) {
JsVar *idx = jsvGetArrayIndexOf(arr, file.fileVar, true);
@ -190,41 +185,50 @@ void jswrap_file_close(JsVar* parent) {
}
}
void _closeFile(JsFile* file) {
if(file) {
#ifndef LINUX
f_close(&file->data.handle);
#else
fclose(file->data.handle);
file->data.handle = 0;
#endif
file->data.state = FS_CLOSED;
fileSetVar(file);
}
}
/*JSON{ "type" : "method", "class" : "File", "name" : "write",
"generate" : "jswrap_file_write",
"description" : [ "write data to a file in byte size chunks"],
"params" : [ ["buffer", "JsVar", "an array to use for storing bytes read."],
["length", "int32", "is an integer specifying the number of bytes to write."],
["position", "int32", "is an integer specifying where to begin writing to in the file.", "If position is null, data will be written from the current file position."],
["callback", "JsVar", "a function to call when the data has been written."]],
"description" : [ "write data to a file"],
"params" : [ ["buffer", "JsVar", "A string containing the bytes to write"] ],
"return" : [ "int32", "the number of bytes written" ]
}*/
//fs.write(fd, buffer, offset, length, position, callback)
size_t jswrap_file_write(JsVar* parent, JsVar* buffer, int length, int position, JsVar* callback) {
size_t jswrap_file_write(JsVar* parent, JsVar* buffer) {
FRESULT res = 0;
size_t bytesWritten = 0;
if (jsfsInit()) {
JsFile file;
if (fileGetFromVar(&file, parent)) {
bytesWritten = file.write(&file, buffer, length, position, &res);
}
if(callback) {
JsVar *bytesWrittenVar = jsvNewFromInteger((JsVarInt)bytesWritten);
jsiQueueEvents(callback, &bytesWrittenVar, 1);
jsvUnLock(bytesWrittenVar);
if(file.data.mode == FM_WRITE || file.data.mode == FM_READ_WRITE) {
JsvIterator it;
jsvIteratorNew(&it, buffer);
char buf[32];
while (jsvIteratorHasElement(&it)) {
// pull in a buffer's worth of data
size_t n = 0;
while (jsvIteratorHasElement(&it) && n<sizeof(buf)) {
buf[n++] = (char)jsvIteratorGetIntegerValue(&it);
jsvIteratorNext(&it);
}
// write it out
size_t written = 0;
#ifndef LINUX
res = f_write(&file.data.handle, &buf, n, &written);
#else
written = fwrite(&buf, 1, n, file.data.handle);
#endif
bytesWritten += written;
if(written == 0)
res = FR_DISK_ERR;
if (res) break;
}
jsvIteratorFree(&it);
// finally, sync - just in case there's a reset or something
#ifndef LINUX
f_sync(&file.data.handle);
#else
fflush(file.data.handle);
#endif
}
}
}
@ -234,108 +238,72 @@ size_t jswrap_file_write(JsVar* parent, JsVar* buffer, int length, int position,
return bytesWritten;
}
size_t _writeFile(JsFile* file, JsVar* buffer, int length, int position, FRESULT* res) {
size_t bytesWritten = 0;
if(file->data.mode == FM_WRITE || file->data.mode == FM_READ_WRITE) {
if(position >= 0) {
#ifndef LINUX
f_lseek(&file->data.handle, position);
#else
fseek(file->data.handle, position, SEEK_SET);
#endif
}
JsvStringIterator it;
JsVar *dataString = jsvSkipName(buffer);
jsvStringIteratorNew(&it, dataString, 0);
size_t written = 0;
char buf = '\0';
int i =0;
for(i=0; i<length; i++) {
if(!jsvStringIteratorHasChar(&it) || *res!=FR_OK) {
break;
}
buf = jsvStringIteratorGetChar(&it);
#ifndef LINUX
*res = f_write(&file->data.handle, &buf, sizeof(buf), &written);
f_sync(&file->data.handle);
#else
written = fwrite(&buf, sizeof(buf), sizeof(buf), file->data.handle);
fflush(file->data.handle);
if(written == 0) {
*res = FR_DISK_ERR;
}
#endif
bytesWritten += written;
jsvStringIteratorNext(&it);
}
jsvStringIteratorFree(&it);
jsvUnLock(dataString);
}
return bytesWritten;
}
/*JSON{ "type" : "method", "class" : "File", "name" : "read",
"generate" : "jswrap_file_read",
"description" : [ "Read data in a file in byte size chunks"],
"params" : [ ["buffer", "JsVar", "an array to use for storing bytes read."],
["length", "int32", "is an integer specifying the number of bytes to read."],
["position", "int32", "is an integer specifying where to begin reading from in the file.", "If position is null, data will be read from the current file position."],
["callback", "JsVar", "a function to call when the data has been read."]],
"return" : [ "int32", "the number of bytes read" ]
"params" : [ ["length", "int32", "is an integer specifying the number of bytes to read."] ],
"return" : [ "JsVar", "A string containing the characters that were read" ]
}*/
//fs.read(fd, buffer, length, position, callback)
size_t jswrap_file_read(JsVar* parent, JsVar* buffer, int length, int position, JsVar* callback) {
JsVar *jswrap_file_read(JsVar* parent, int length) {
JsVar *buffer = 0;
FRESULT res = 0;
size_t bytesRead = 0;
if (jsfsInit()) {
JsFile file;
if (fileGetFromVar(&file, parent)) {
bytesRead = file.read(&file, buffer, length, position, &res);
}
if(callback) {
JsVar *bytesReadVar = jsvNewFromInteger((JsVarInt)bytesRead);
jsiQueueEvents(callback, &bytesReadVar, 1);
jsvUnLock(bytesReadVar);
if(file.data.mode == FM_READ || file.data.mode == FM_READ_WRITE) {
char buf[32];
size_t i = 0;
size_t actual = 0;
for(i=0; (int)i < length; i += actual) {
size_t requested = (size_t)length - i;
if (requested > sizeof( buf ))
requested = sizeof( buf );
actual = 0;
#ifndef LINUX
res = f_read(&file.data.handle, buf, requested, &actual);
if(res) break;
#else
actual = fread(buf, 1, requested, file.data.handle);
#endif
if (!buffer) {
buffer = jsvNewFromEmptyString();
if (!buffer) return 0; // out of memory
}
jsvAppendStringBuf(buffer, buf, actual);
if(actual != requested) break;
}
}
}
}
if (res) {
jsfsReportError("Unable to read file", res);
}
return bytesRead;
if (res) jsfsReportError("Unable to read file", res);
return buffer;
}
size_t _readFile(JsFile* file, JsVar* buffer, int length, int position, FRESULT* res) {
size_t bytesRead = 0;
if(file->data.mode == FM_READ || file->data.mode == FM_READ_WRITE) {
if(position >= 0) {
#ifndef LINUX
f_lseek(&file->data.handle, position);
#else
fseek(file->data.handle, position, SEEK_SET);
#endif
}
size_t readBytes=0;
char buf = '\0';
int i = 0;
for(i=0; i < length;i++) {
#ifndef LINUX
*res = f_read(&file->data.handle, &buf, sizeof( buf ), &readBytes);
if(*res != FR_OK) {
break;
/*JSON{ "type" : "method", "class" : "File", "name" : "skip",
"generate" : "jswrap_file_skip",
"description" : [ "Skip the specified number of bytes forwards"],
"params" : [ ["nBytes", "int32", "is an integer specifying the number of bytes to skip forwards."] ]
}*/
void jswrap_file_skip(JsVar* parent, int length) {
if (length<=0) {
jsWarn("length for skip must be greater than 0");
return;
}
FRESULT res = 0;
if (jsfsInit()) {
JsFile file;
if (fileGetFromVar(&file, parent)) {
if(file.data.mode == FM_READ || file.data.mode == FM_READ_WRITE) {
#ifndef LINUX
res = f_lseek(&file.data.handle, f_tell(&file.data.handle) + length);
#else
fseek(file.data.handle, length, SEEK_CUR);
#endif
}
#else
readBytes = fread(&buf,sizeof( buf ), sizeof( buf ), file->data.handle);
if(readBytes > 0) {
*res = FR_OK;
}
#endif
jsvAppendStringBuf(buffer, &buf, 1);
bytesRead += readBytes;
}
}
return bytesRead;
if (res) jsfsReportError("Unable to skip", res);
}
/*JSON{ "type" : "method", "class" : "File", "name" : "pipe",

View File

@ -61,18 +61,15 @@ typedef struct JsFile {
JsVar* fileVar; // this won't be locked again - we just know that it is already locked by something else
JsFileData data;
unsigned char _blank; //< this is needed as jsvGetString for 'data' wants to add a trailing zero
size_t (*read)(struct JsFile* file, JsVar* buffer, int length, int position, FRESULT* res);
size_t (*write)(struct JsFile* file, JsVar* buffer, int length, int position, FRESULT* res);
void (*close)(struct JsFile* file);
} PACKED_FLAGS JsFile;
// Called when stopping, to make sure all files are closed
void jswrap_file_kill();
JsVar* jswrap_file_constructor(JsVar* path, JsVar* mode);
size_t jswrap_file_read(JsVar* parent, JsVar* buffer, int length, int position, JsVar* callback);
size_t jswrap_file_write(JsVar* parent, JsVar* buffer, int length, int position, JsVar* callback);
JsVar* jswrap_file_pipe(JsVar* parent, JsVar* destfd, JsVar* chunkSize, JsVar* callback);
size_t jswrap_file_write(JsVar* parent, JsVar* buffer);
JsVar *jswrap_file_read(JsVar* parent, int length);
void jswrap_file_skip(JsVar* parent, int length);
void jswrap_file_close(JsVar* parent);
//var r = fs.createReadStream('file.txt');

View File

@ -33,44 +33,31 @@ JsVar* PipeGetArray(const char *name, bool create) {
}
static bool _pipe(JsVar* source, JsVar* destination, JsVar* chunkSize, JsVar* position) {
JsVarInt Bytes_Read = 0;
bool dataTransferred = false;
if(source && destination && chunkSize && position) {
JsVar * Buffer = jsvNewFromEmptyString();
JsVarInt Position = jsvGetInteger(position);
if(Buffer) {// do we have enough memory?
JsVar *ReadFunc = jspGetNamedField(source, "read");
JsVar *WriteFunc = jspGetNamedField(destination, "write");
if (jsvIsFunction(ReadFunc) && jsvIsFunction(WriteFunc)) { // do the objects have the necessary methods on them?
JsVar *ReadArgs[3];
ReadArgs[0] = Buffer;
ReadArgs[1] = chunkSize;
ReadArgs[2] = position;
JsVar *BytesRead = jspExecuteFunction(ReadFunc, source, 3, ReadArgs);
Bytes_Read = jsvGetInteger(BytesRead);
if(Bytes_Read > 0)
{
JsVar *WriteArgs[3];
WriteArgs[0] = Buffer;
WriteArgs[1] = BytesRead;
WriteArgs[2] = position;
jsvUnLock(jspExecuteFunction(WriteFunc, destination, 3, WriteArgs));
jsvSetInteger(position, (Position+Bytes_Read));
}
jsvUnLock(BytesRead);
} else {
if(!jsvIsFunction(ReadFunc)) {
jsError("Source Stream does not implement the required read(buffer, length, position) method.");
}
if(!jsvIsFunction(WriteFunc)) {
jsError("Destination Stream does not implement the required write(buffer, length, position) method.");
JsVar *readFunc = jspGetNamedField(source, "read");
JsVar *writeFunc = jspGetNamedField(destination, "write");
if (jsvIsFunction(readFunc) && jsvIsFunction(writeFunc)) { // do the objects have the necessary methods on them?
JsVar *buffer = jspExecuteFunction(readFunc, source, 1, &chunkSize);
if(buffer) {
JsVarInt bufferSize = jsvGetLength(buffer);
if (bufferSize>0) {
jsvUnLock(jspExecuteFunction(writeFunc, destination, 1, &buffer));
jsvSetInteger(position, jsvGetInteger(position) + bufferSize);
dataTransferred = true;
}
jsvUnLock(buffer);
}
jsvUnLock(ReadFunc);
jsvUnLock(WriteFunc);
jsvUnLock(Buffer);
} else {
if(!jsvIsFunction(readFunc))
jsError("Source Stream does not implement the required read(length) method.");
if(!jsvIsFunction(writeFunc))
jsError("Destination Stream does not implement the required write(buffer) method.");
}
jsvUnLock(readFunc);
jsvUnLock(writeFunc);
}
return Bytes_Read > 0;
return dataTransferred;
}
/*JSON{ "type":"idle", "generate" : "jswrap_pipe_idle" }*/
@ -127,7 +114,6 @@ void jswrap_pipe_kill() {
"chunkSize : The amount of data to pipe from source to destination at a time",
"complete : a function to call when the pipe activity is complete"] ] ]
}*/
//fs.pipe(source,destination,4,onCompleteHandler);
void jswrap_pipe(JsVar* source, JsVar* dest, JsVar* options) {
if (!source || !dest) return;
JsVar *pipe = jspNewObject(0, "Pipe");

View File

@ -925,7 +925,7 @@ void jsvAppendString(JsVar *var, const char *str) {
}
// Append the given string to this one - but does not use null-terminated strings. returns false on failure (from out of memory)
bool jsvAppendStringBuf(JsVar *var, const char *str, int length) {
bool jsvAppendStringBuf(JsVar *var, const char *str, size_t length) {
assert(jsvIsString(var));
JsVar *block = jsvLockAgain(var);
// Find the block at end of the string...

View File

@ -286,7 +286,7 @@ bool jsvIsStringEqual(JsVar *var, const char *str);
int jsvCompareString(JsVar *va, JsVar *vb, size_t starta, size_t startb, bool equalAtEndOfString); ///< Compare 2 strings, starting from the given character positions
int jsvCompareInteger(JsVar *va, JsVar *vb); ///< Compare 2 integers, >0 if va>vb, <0 if va<vb. If compared with a non-integer, that gets put later
void jsvAppendString(JsVar *var, const char *str); ///< Append the given string to this one
bool jsvAppendStringBuf(JsVar *var, const char *str, int length); ///< Append the given string to this one - but does not use null-terminated strings. returns false on failure (from out of memory)
bool jsvAppendStringBuf(JsVar *var, const char *str, size_t length); ///< Append the given string to this one - but does not use null-terminated strings. returns false on failure (from out of memory)
void jsvAppendPrintf(JsVar *var, const char *fmt, ...); ///< Append the formatted string to a variable (see vcbprintf)
static inline void jsvAppendCharacter(JsVar *var, char ch) { jsvAppendStringBuf(var, &ch, 1); }; ///< Append the given character to this string
#define JSVAPPENDSTRINGVAR_MAXLENGTH (0x7FFFFFFF)

View File

@ -1,7 +1,7 @@
// read and write both take a number of bytes and position as the second and third arguments.
// 0 is a valid position, so -1 is used to indicate "don't care" or "current position".
var fd = new File('./tests/FS_API_Test.txt','r');
var buffer="";
fd.read(buffer,6,22);
fd.skip(22);
var buffer = fd.read(6);
fd.close();
result = (buffer == "FS API");

View File

@ -1,11 +1,10 @@
// read and write both take a number of bytes and position as the second and third arguments.
// 0 is a valid position, so -1 is used to indicate "don't care" or "current position".
var fd = new File('./tests/FS_API_Write_Test.txt','w');
var buffer1="Testing Write";
fd.write(buffer1,13,0);
var buffer1 = "Testing Write";
fd.write(buffer1);
fd.close();
var fd = new File('./tests/FS_API_Write_Test.txt','r');
var buffer2="";
fd.read(buffer2,13,0);
var buffer2 = fd.read(13);
fd.close();
result = (buffer1 == buffer2);

View File

@ -1,13 +1,11 @@
var fsr = new File('./tests/FS_API_Test.txt', "r");
var fsw = new File('./tests/FS_API_WriteStream_Test.txt', "w");
var buffer1="";
var buffer2="";
fsr.read(buffer1,6,22);
fsw.write(buffer1,6,0);
fsr.skip(22);
var buffer1 = fsr.read(6);
fsw.write(buffer1);
fsw.close();
fsr.close();
var fsr2 = new File('./tests/FS_API_WriteStream_Test.txt', "r");
fsr2.read(buffer2,6,0);
var buffer2 = fsr2.read(6);
fsr2.close();
result = (buffer1 == "FS API" && buffer1 == buffer2);

View File

@ -1,6 +1,6 @@
var fsr = new File('test.txt','r');
var fsw = new File('test_out.txt','w');
fsr.pipe(fsw,{ chunkSize:8, complete:function(pipe) {
fsr.pipe(fsw, { chunkSize:8, complete:function(pipe) {
pipe.source.close();
pipe.destination.close();