Skip to content

Commit

Permalink
Allow E.pipe to pipe from Strings, remove pipe's internal position
Browse files Browse the repository at this point in the history
…counter (fix #2352)
  • Loading branch information
gfwilliams committed Aug 16, 2023
1 parent 681f115 commit ca68d68
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 18 deletions.
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
Bangle.js2: Ensure `HRM-raw.raw` is set correctly after we moved to binary hrm algorithm
nRF5x: Add `NRF.on("passkey", ...)` to allow passkey pairing with `NRF.setSecurity({mitm:1, display:1});`
nrf52: Allow disabling pairing with `NRF.setSecurity({pair:0})`
Allow E.pipe to pipe from Strings, remove pipe's internal `position` counter (fix #2352)

2v18 : Fix drawString with setClipRect with 90/270-degree rotation (fix #2343)
Pico/Wifi: Enabled JIT compiler
Expand Down
36 changes: 34 additions & 2 deletions src/jswrap_espruino.c
Original file line number Diff line number Diff line change
Expand Up @@ -777,12 +777,44 @@ their values.
"ifndef" : "SAVE_ON_FLASH",
"generate" : "jswrap_pipe",
"params" : [
["source","JsVar","The source file/stream that will send content."],
["source","JsVar","The source file/stream that will send content. As of 2v19 this can also be a `String`"],
["destination","JsVar","The destination file/stream that will receive content from the source."],
["options","JsVar",["[optional] An object `{ chunkSize : int=64, end : bool=true, complete : function }`","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","end : call the 'end' function on the destination when the source is finished"]]
],
"typescript" : "pipe(source: any, destination: any, options?: PipeOptions): void"
}*/
}
Pipe one stream to another.
This can be given any object with a `read` method as a source, and any object with a `.write(data)` method as a destination.
Data will be piped from `source` to `destination` in the idle loop until `source.read(...)` returns `undefined`.
For instance:
```
// Print a really big string to the console, 1 character at a time and write 'Finished!' at the end
E.pipe("This is a really big String",
{write: print},
{chunkSize:1, complete:()=>print("Finished!")});
// Pipe the numbers 1 to 100 to a StorageFile in Storage
E.pipe({ n:0, read : function() { if (this.n<100) return (this.n++)+"\n"; }},
require("Storage").open("testfile","w"));
// Pipe a StorageFile straight to the Bluetooth UART
E.pipe(require("Storage").open("testfile","r"), Bluetooth);
// Pipe a normal file in Storage (not StorageFile) straight to the Bluetooth UART
E.pipe(require("Storage").read("blob.txt"), Bluetooth);
// Pipe a normal file in Storage as a response to an HTTP request
function onPageRequest(req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
E.pipe(require("Storage").read("webpage.txt"), res);
}
require("http").createServer(onPageRequest).listen(80);
```
*/

/*JSON{
"type" : "staticmethod",
Expand Down
31 changes: 15 additions & 16 deletions src/jswrap_pipe.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,6 @@ static void handlePipeClose(JsVar *arr, JsvObjectIterator *it, JsVar* pipe) {
jsvUnLock(jspExecuteFunction(writeFunc, destination, 1, &buffer));
}
jsvUnLock(writeFunc);
// update position
JsVar *position = jsvObjectGetChildIfExists(pipe,"position");
jsvSetInteger(position, jsvGetInteger(position) + (JsVarInt)jsvGetStringLength(buffer));
jsvUnLock(position);
}
jsvUnLock(buffer);
}
Expand Down Expand Up @@ -104,13 +100,12 @@ static bool handlePipe(JsVar *arr, JsvObjectIterator *it, JsVar* pipe) {
bool paused = jsvGetBoolAndUnLock(jsvObjectGetChildIfExists(pipe,"drainWait"));
if (paused) return false;

JsVar *position = jsvObjectGetChildIfExists(pipe,"position");
JsVar *chunkSize = jsvObjectGetChildIfExists(pipe,"chunkSize");
JsVar *source = jsvObjectGetChildIfExists(pipe,"source");
JsVar *destination = jsvObjectGetChildIfExists(pipe,"destination");

bool dataTransferred = false;
if(source && destination && chunkSize && position) {
if(source && destination && chunkSize) {
JsVar *readFunc = jspGetNamedField(source, "read", false);
JsVar *writeFunc = jspGetNamedField(destination, "write", false);
if (jsvIsFunction(readFunc) && jsvIsFunction(writeFunc)) { // do the objects have the necessary methods on them?
Expand All @@ -124,7 +119,6 @@ static bool handlePipe(JsVar *arr, JsvObjectIterator *it, JsVar* pipe) {
jsvObjectSetChildAndUnLock(pipe,"drainWait",jsvNewFromBool(true));
}
jsvUnLock(response);
jsvSetInteger(position, jsvGetInteger(position) + bufferSize);
}
jsvUnLock(buffer);
dataTransferred = true; // so we don't close the pipe if we get an empty string
Expand All @@ -142,7 +136,6 @@ static bool handlePipe(JsVar *arr, JsvObjectIterator *it, JsVar* pipe) {
handlePipeClose(arr, it, pipe);
}
jsvUnLock3(source, destination, chunkSize);
jsvUnLock(position);
return dataTransferred;
}

Expand Down Expand Up @@ -256,13 +249,20 @@ type PipeOptions = {
["options","JsVar",["[optional] An object `{ chunkSize : int=64, end : bool=true, complete : function }`","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","end : call the 'end' function on the destination when the source is finished"]]
],
"typescript": "pipe(destination: any, options?: PipeOptions): void"
}*/
}
Pipe this file to a destination stream (object which has a `.write(data)` method).
*/
void jswrap_pipe(JsVar* source, JsVar* dest, JsVar* options) {
if (!source || !dest) return;
jsvLockAgain(source); // source should be unlocked at end
JsVar *pipe = jspNewObject(0, "Pipe");
JsVar *arr = pipeGetArray(true);
JsVar* position = jsvNewFromInteger(0);
if (pipe && arr && position) {// out of memory?
if (pipe && arr) {// out of memory?
if (jsvIsString(source)) { // Single-line 'StringStream' object to add ability to stream from Strings
JsVar *stream = jspExecuteJSFunction("(function(s){var p=0;return{read:function(l){return s.substring(p,p+=l)||undefined;}}})",NULL,1,&source);
jsvUnLock(source);
source = stream;
}
JsVar *readFunc = jspGetNamedField(source, "read", false);
JsVar *writeFunc = jspGetNamedField(dest, "write", false);
if(jsvIsFunction(readFunc)) {
Expand Down Expand Up @@ -297,19 +297,18 @@ void jswrap_pipe(JsVar* source, JsVar* dest, JsVar* options) {
// set up the rest of the pipe
jsvObjectSetChildAndUnLock(pipe, "chunkSize", jsvNewFromInteger(chunkSize));
jsvObjectSetChildAndUnLock(pipe, "end", jsvNewFromBool(callEnd));
jsvUnLock3(jsvAddNamedChild(pipe, position, "position"),
jsvAddNamedChild(pipe, source, "source"),
jsvUnLock2(jsvAddNamedChild(pipe, source, "source"),
jsvAddNamedChild(pipe, dest, "destination"));
// add the pipe to our list
jsvArrayPush(arr, pipe);
} else {
jsExceptionHere(JSET_ERROR, "Destination object does not implement the required write(buffer, length, position) method.");
jsExceptionHere(JSET_ERROR, "Destination object does not implement the required write(buffer, length) method.");
}
} else {
jsExceptionHere(JSET_ERROR, "Source object does not implement the required read(buffer, length, position) method.");
jsExceptionHere(JSET_ERROR, "Source object does not implement the required read(buffer, length) method.");
}
jsvUnLock2(readFunc, writeFunc);
}
jsvUnLock3(arr, pipe, position);
jsvUnLock3(arr, pipe, source);
}

0 comments on commit ca68d68

Please sign in to comment.