JsVar *jslNewStringFromLexer(JslCharPos *charFrom, size_t charTo) { // Original method - just copy it verbatim size_t maxLength = charTo + 1 - jsvStringIteratorGetIndex(&charFrom->it); assert(maxLength>0); // will fail if 0 // Try and create a flat string first JsVar *var = 0; if (maxLength > JSV_FLAT_STRING_BREAK_EVEN) { var = jsvNewFlatStringOfLength((unsigned int)maxLength); if (var) { // Flat string char *flatPtr = jsvGetFlatStringPointer(var); *(flatPtr++) = charFrom->currCh; JsvStringIterator it = jsvStringIteratorClone(&charFrom->it); while (jsvStringIteratorHasChar(&it) && (--maxLength>0)) { *(flatPtr++) = jsvStringIteratorGetChar(&it); jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); return var; } } // Non-flat string... var = jsvNewFromEmptyString(); if (!var) { // out of memory return 0; } //jsvAppendStringVar(var, lex->sourceVar, charFrom->it->index, (int)(charTo-charFrom)); JsVar *block = jsvLockAgain(var); block->varData.str[0] = charFrom->currCh; size_t blockChars = 1; size_t l = maxLength; // now start appending JsvStringIterator it = jsvStringIteratorClone(&charFrom->it); while (jsvStringIteratorHasChar(&it) && (--maxLength>0)) { char ch = jsvStringIteratorGetChar(&it); if (blockChars >= jsvGetMaxCharactersInVar(block)) { jsvSetCharactersInVar(block, blockChars); JsVar *next = jsvNewWithFlags(JSV_STRING_EXT_0); if (!next) break; // out of memory // we don't ref, because StringExts are never reffed as they only have one owner (and ALWAYS have an owner) jsvSetLastChild(block, jsvGetRef(next)); jsvUnLock(block); block = next; blockChars=0; // it's new, so empty } block->varData.str[blockChars++] = ch; jsvStringIteratorNext(&it); } jsvSetCharactersInVar(block, blockChars); jsvUnLock(block); // Just make sure we only assert if there's a bug here. If we just ran out of memory or at end of string it's ok assert((l == jsvGetStringLength(var)) || (jsErrorFlags&JSERR_MEMORY) || !jsvStringIteratorHasChar(&it)); jsvStringIteratorFree(&it); return var; }
/*JSON{ "type" : "method", "class" : "String", "name" : "trim", "generate" : "jswrap_string_trim", "return" : ["JsVar","A String with Whitespace removed from the beginning and end"], "return_object" : "String" } Return a new string with any whitespace (tabs, space, form feed, newline, carriage return, etc) removed from the beginning and end. */ JsVar *jswrap_string_trim(JsVar *parent) { JsVar *s = jsvAsString(parent); if (!s) return s; unsigned int start = 0; int end = -1; // work out beginning and end JsvStringIterator it; jsvStringIteratorNew(&it, s, 0); while (jsvStringIteratorHasChar(&it)) { bool ws = isWhitespace(jsvStringIteratorGetChar(&it)); if (!ws) { if (end<0) start = (unsigned int)jsvStringIteratorGetIndex(&it); end = (int)jsvStringIteratorGetIndex(&it); // last } jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); // work out length unsigned int len = 0; if (end>=(int)start) len = 1+(unsigned int)end-start; JsVar *res = jsvNewFromStringVar(s, start, len); jsvUnLock(s); return res; }
/* match: search for regexp anywhere in text */ static JsVar *match(char *regexp, JsVar *str, size_t startIndex, bool ignoreCase) { matchInfo info; info.sourceStr = str; info.startIndex = startIndex; info.ignoreCase = ignoreCase; info.rangeMatch = false; info.rangeFirstChar = NO_RANGE; info.groups = 0; JsVar *rmatch; JsvStringIterator txtIt, txtIt2; jsvStringIteratorNew(&txtIt, str, startIndex); /* must look even if string is empty */ txtIt2 = jsvStringIteratorClone(&txtIt); rmatch = matchhere(regexp, &txtIt2, info); jsvStringIteratorFree(&txtIt2); jsvStringIteratorNext(&txtIt); while (!rmatch && jsvStringIteratorHasChar(&txtIt)) { info.startIndex++; txtIt2 = jsvStringIteratorClone(&txtIt); rmatch = matchhere(regexp, &txtIt2, info); jsvStringIteratorFree(&txtIt2); jsvStringIteratorNext(&txtIt); } jsvStringIteratorFree(&txtIt); return rmatch; }
bool jsvIteratorHasElement(JsvIterator *it) { switch (it->type) { case JSVI_OBJECT : return jsvObjectIteratorHasValue(&it->it.obj); case JSVI_STRING : return jsvStringIteratorHasChar(&it->it.str); case JSVI_ARRAYBUFFER : return jsvArrayBufferIteratorHasElement(&it->it.buf); default: assert(0); return 0; } }
void jsvStringIteratorAppendString(JsvStringIterator *it, JsVar *str, size_t startIdx) { JsvStringIterator sit; jsvStringIteratorNew(&sit, str, startIdx); while (jsvStringIteratorHasChar(&sit)) { jsvStringIteratorAppend(it, jsvStringIteratorGetChar(&sit)); jsvStringIteratorNext(&sit); } jsvStringIteratorFree(&sit); }
void jslPrintTokenLineMarker(vcbprintf_callback user_callback, void *user_data, size_t tokenPos, char *prefix) { size_t line = 1,col = 1; jsvGetLineAndCol(lex->sourceVar, tokenPos, &line, &col); size_t startOfLine = jsvGetIndexFromLineAndCol(lex->sourceVar, line, 1); size_t lineLength = jsvGetCharsOnLine(lex->sourceVar, line); size_t prefixLength = 0; if (prefix) { user_callback(prefix, user_data); prefixLength = strlen(prefix); } if (lineLength>60 && tokenPos-startOfLine>30) { cbprintf(user_callback, user_data, "..."); size_t skipChars = tokenPos-30 - startOfLine; startOfLine += 3+skipChars; if (skipChars<=col) col -= skipChars; else col = 0; lineLength -= skipChars; } // print the string until the end of the line, or 60 chars (whichever is less) int chars = 0; JsvStringIterator it; jsvStringIteratorNew(&it, lex->sourceVar, startOfLine); unsigned char lastch = 0; while (jsvStringIteratorHasChar(&it) && chars<60) { unsigned char ch = (unsigned char)jsvStringIteratorGetChar(&it); if (ch == '\n') break; if (jslNeedSpaceBetween(lastch, ch)) { col++; user_callback(" ", user_data); } char buf[32]; jslFunctionCharAsString(ch, buf, sizeof(buf)); size_t len = strlen(buf); col += len-1; user_callback(buf, user_data); chars++; lastch = ch; jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); if (lineLength > 60) user_callback("...", user_data); user_callback("\n", user_data); col += prefixLength; while (col-- > 1) user_callback(" ", user_data); user_callback("^\n", user_data); }
void jsfGetEscapedString(JsVar *var, vcbprintf_callback user_callback, void *user_data) { user_callback("\"",user_data); JsvStringIterator it; jsvStringIteratorNew(&it, var, 0); while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); user_callback(escapeCharacter(ch), user_data); jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); user_callback("\"",user_data); }
/*JSON{ "type" : "method", "class" : "String", "name" : "charAt", "generate" : "jswrap_string_charAt", "params" : [ ["pos","int","The character number in the string. Negative values return characters from end of string (-1 = last char)"] ], "return" : ["JsVar","The character in the string"] } Return a single character at the given position in the String. */ JsVar *jswrap_string_charAt(JsVar *parent, JsVarInt idx) { // We do this so we can handle '/0' in a string JsVar *r = jsvNewFromEmptyString(); if (r && jsvIsString(parent) && idx>=0) { JsvStringIterator it; jsvStringIteratorNew(&it, parent, (size_t)idx); if (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); jsvAppendStringBuf(r, &ch, 1); } jsvStringIteratorFree(&it); } return r; }
size_t httpStringGet(JsVar *v, char *str, size_t len) { size_t l = len; JsvStringIterator it; jsvStringIteratorNew(&it, v, 0); while (jsvStringIteratorHasChar(&it)) { if (l--==0) { jsvStringIteratorFree(&it); return len; } *(str++) = jsvStringIteratorGetChar(&it); jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); return len-l; }
/// Does this regex have the given flag? bool jswrap_regexp_hasFlag(JsVar *parent, char flag) { JsVar *flags = jsvObjectGetChild(parent, "flags", 0); bool has = false; if (jsvIsString(flags)) { JsvStringIterator it; jsvStringIteratorNew(&it, flags, 0); while (jsvStringIteratorHasChar(&it)) { has |= jsvStringIteratorGetChar(&it)==flag; jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); } jsvUnLock(flags); return has; }
void jslPrintTokenLineMarker(vcbprintf_callback user_callback, void *user_data, size_t tokenPos, char *prefix) { size_t line = 1,col = 1; jsvGetLineAndCol(lex->sourceVar, tokenPos, &line, &col); size_t startOfLine = jsvGetIndexFromLineAndCol(lex->sourceVar, line, 1); size_t lineLength = jsvGetCharsOnLine(lex->sourceVar, line); size_t prefixLength = 0; if (prefix) { user_callback(prefix, user_data); prefixLength = strlen(prefix); } if (lineLength>60 && tokenPos-startOfLine>30) { cbprintf(user_callback, user_data, "..."); size_t skipChars = tokenPos-30 - startOfLine; startOfLine += 3+skipChars; col -= skipChars; lineLength -= skipChars; } // print the string until the end of the line, or 60 chars (whichever is lesS) int chars = 0; JsvStringIterator it; jsvStringIteratorNew(&it, lex->sourceVar, startOfLine); while (jsvStringIteratorHasChar(&it) && chars<60) { char ch = jsvStringIteratorGetChar(&it); if (ch == '\n') break; char buf[2]; buf[0] = ch; buf[1] = 0; user_callback(buf, user_data); chars++; jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); if (lineLength > 60) user_callback("...", user_data); user_callback("\n", user_data); col += prefixLength; while (col-- > 1) user_callback(" ", user_data); user_callback("^\n", user_data); }
/** Iterate over the contents of var, calling callback for each. Contents may be: * * numeric -> output * * a string -> output each character * * array/arraybuffer -> call itself on each element * * object -> call itself object.count times, on object.data */ bool jsvIterateCallback(JsVar *data, void (*callback)(int item, void *callbackData), void *callbackData) { bool ok = true; if (jsvIsNumeric(data)) { callback((int)jsvGetInteger(data), callbackData); } else if (jsvIsObject(data)) { JsVar *countVar = jsvObjectGetChild(data, "count", 0); JsVar *dataVar = jsvObjectGetChild(data, "data", 0); if (countVar && dataVar && jsvIsNumeric(countVar)) { int n = (int)jsvGetInteger(countVar); while (ok && n-- > 0) { ok = jsvIterateCallback(dataVar, callback, callbackData); } } else { jsWarn("If specifying an object, it must be of the form {data : ..., count : N}"); } jsvUnLock(countVar); jsvUnLock(dataVar); } else if (jsvIsString(data)) { JsvStringIterator it; jsvStringIteratorNew(&it, data, 0); while (jsvStringIteratorHasChar(&it) && ok) { char ch = jsvStringIteratorGetChar(&it); callback(ch, callbackData); jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); } else if (jsvIsIterable(data)) { JsvIterator it; jsvIteratorNew(&it, data); while (jsvIteratorHasElement(&it) && ok) { JsVar *el = jsvIteratorGetValue(&it); ok = jsvIterateCallback(el, callback, callbackData); jsvUnLock(el); jsvIteratorNext(&it); } jsvIteratorFree(&it); } else { jsWarn("Expecting a number or something iterable, got %t", data); ok = false; } return ok; }
/*JSON{ "type":"function", "name" : "isNaN", "description" : "Whether the x is NaN (Not a Number) or not", "generate" : "jswrap_isNaN", "params" : [ [ "x", "JsVar", ""] ], "return" : ["bool", "True is the value is NaN, false if not."] }*/ bool jswrap_isNaN(JsVar *v) { if (jsvIsUndefined(v) || jsvIsObject(v) || (jsvIsFloat(v) && isnan(jsvGetFloat(v)))) return true; if (jsvIsString(v)) { // this is where is can get a bit crazy bool allWhiteSpace = true; JsvStringIterator it; jsvStringIteratorNew(&it,v,0); while (jsvStringIteratorHasChar(&it)) { if (!isWhitespace(jsvStringIteratorGetChar(&it))) { allWhiteSpace = false; break; } jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); if (allWhiteSpace) return false; return isnan(jsvGetFloat(v)); } return false; }
/*JSON{ "type" : "method", "class" : "String", "name" : "toUpperCase", "generate_full" : "jswrap_string_toUpperLowerCase(parent, true)", "params" : [ ], "return" : ["JsVar","The uppercase version of this string"] }*/ JsVar *jswrap_string_toUpperLowerCase(JsVar *parent, bool upper) { JsVar *res = jsvNewFromEmptyString(); if (!res) return 0; // out of memory JsVar *parentStr = jsvAsString(parent); JsvStringIterator itsrc, itdst; jsvStringIteratorNew(&itsrc, parentStr, 0); jsvStringIteratorNew(&itdst, res, 0); while (jsvStringIteratorHasChar(&itsrc)) { char ch = jsvStringIteratorGetChar(&itsrc); ch = upper ? jsvStringCharToUpper(ch) : jsvStringCharToLower(ch); jsvStringIteratorAppend(&itdst, ch); jsvStringIteratorNext(&itsrc); } jsvStringIteratorFree(&itsrc); jsvStringIteratorFree(&itdst); jsvUnLock(parentStr); return res; }
JsVar *jslNewFromLexer(struct JsLex *lex, JslCharPos *charFrom, size_t charTo) { // Create a var JsVar *var = jsvNewFromEmptyString(); if (!var) { // out of memory return 0; } //jsvAppendStringVar(var, lex->sourceVar, charFrom->it->index, (int)(charTo-charFrom)); size_t maxLength = charTo - jsvStringIteratorGetIndex(&charFrom->it); JsVar *block = jsvLockAgain(var); block->varData.str[0] = charFrom->currCh; size_t blockChars = 1; // now start appending JsvStringIterator it = jsvStringIteratorClone(&charFrom->it); while (jsvStringIteratorHasChar(&it) && (maxLength-->0)) { char ch = jsvStringIteratorGetChar(&it); if (blockChars >= jsvGetMaxCharactersInVar(block)) { jsvSetCharactersInVar(block, blockChars); JsVar *next = jsvNewWithFlags(JSV_STRING_EXT); if (!next) break; // out of memory // we don't ref, because StringExts are never reffed as they only have one owner (and ALWAYS have an owner) block->lastChild = jsvGetRef(next); jsvUnLock(block); block = next; blockChars=0; // it's new, so empty } block->varData.str[blockChars++] = ch; jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); jsvSetCharactersInVar(block, blockChars); jsvUnLock(block); return var; }
/*JSON{ "type" : "method", "class" : "String", "name" : "toUpperCase", "generate_full" : "jswrap_string_toUpperLowerCase(parent, true)", "params" : [ ], "return" : ["JsVar","The uppercase version of this string"] }*/ JsVar *jswrap_string_toUpperLowerCase(JsVar *parent, bool upper) { JsVar *res = jsvNewFromEmptyString(); if (!res) return 0; // out of memory JsvStringIterator itsrc, itdst; jsvStringIteratorNew(&itsrc, parent, 0); jsvStringIteratorNew(&itdst, res, 0); while (jsvStringIteratorHasChar(&itsrc)) { char ch = jsvStringIteratorGetChar(&itsrc); if (upper) { if (ch >= 97 && ch <= 122) ch = (char)(ch - 32); } else { if (ch >= 65 && ch <= 90) ch = (char)(ch + 32); // A-Z } jsvStringIteratorAppend(&itdst, ch); jsvStringIteratorNext(&itsrc); } jsvStringIteratorFree(&itsrc); jsvStringIteratorFree(&itdst); return res; }
// httpParseHeaders(&receiveData, reqVar, true) // server // httpParseHeaders(&receiveData, resVar, false) // client bool httpParseHeaders(JsVar **receiveData, JsVar *objectForData, bool isServer) { // find /r/n/r/n int newlineIdx = 0; int strIdx = 0; int headerEnd = -1; JsvStringIterator it; jsvStringIteratorNew(&it, *receiveData, 0); while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); if (ch == '\r') { if (newlineIdx==0) newlineIdx=1; else if (newlineIdx==2) newlineIdx=3; } else if (ch == '\n') { if (newlineIdx==1) newlineIdx=2; else if (newlineIdx==3) { headerEnd = strIdx+1; break; } } else newlineIdx=0; jsvStringIteratorNext(&it); strIdx++; } jsvStringIteratorFree(&it); // skip if we have no header if (headerEnd<0) return false; // Now parse the header JsVar *vHeaders = jsvNewObject(); if (!vHeaders) return true; jsvUnLock(jsvAddNamedChild(objectForData, vHeaders, "headers")); strIdx = 0; int firstSpace = -1; int secondSpace = -1; int firstEOL = -1; int lineNumber = 0; int lastLineStart = 0; int colonPos = 0; //jsiConsolePrintStringVar(receiveData); jsvStringIteratorNew(&it, *receiveData, 0); while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); if (ch==' ' || ch=='\r') { if (firstSpace<0) firstSpace = strIdx; else if (secondSpace<0) secondSpace = strIdx; } if (ch == ':' && colonPos<0) colonPos = strIdx; if (ch == '\r') { if (firstEOL<0) firstEOL=strIdx; if (lineNumber>0 && colonPos>lastLineStart && lastLineStart<strIdx) { JsVar *hVal = jsvNewFromEmptyString(); if (hVal) jsvAppendStringVar(hVal, *receiveData, (size_t)colonPos+2, (size_t)(strIdx-(colonPos+2))); JsVar *hKey = jsvNewFromEmptyString(); if (hKey) { jsvMakeIntoVariableName(hKey, hVal); jsvAppendStringVar(hKey, *receiveData, (size_t)lastLineStart, (size_t)(colonPos-lastLineStart)); jsvAddName(vHeaders, hKey); jsvUnLock(hKey); } jsvUnLock(hVal); } lineNumber++; colonPos=-1; } if (ch == '\r' || ch == '\n') { lastLineStart = strIdx+1; } jsvStringIteratorNext(&it); strIdx++; } jsvStringIteratorFree(&it); jsvUnLock(vHeaders); // try and pull out methods/etc if (isServer) { jsvObjectSetChildAndUnLock(objectForData, "method", jsvNewFromStringVar(*receiveData, 0, (size_t)firstSpace)); jsvObjectSetChildAndUnLock(objectForData, "url", jsvNewFromStringVar(*receiveData, (size_t)(firstSpace+1), (size_t)(secondSpace-(firstSpace+1)))); } else { jsvObjectSetChildAndUnLock(objectForData, "httpVersion", jsvNewFromStringVar(*receiveData, 5, (size_t)firstSpace-5)); jsvObjectSetChildAndUnLock(objectForData, "statusCode", jsvNewFromStringVar(*receiveData, (size_t)(firstSpace+1), (size_t)(secondSpace-(firstSpace+1)))); jsvObjectSetChildAndUnLock(objectForData, "statusMessage", jsvNewFromStringVar(*receiveData, (size_t)(secondSpace+1), (size_t)(firstEOL-(secondSpace+1)))); } // strip out the header JsVar *afterHeaders = jsvNewFromStringVar(*receiveData, (size_t)headerEnd, JSVAPPENDSTRINGVAR_MAXLENGTH); jsvUnLock(*receiveData); *receiveData = afterHeaders; return true; }
/* This is like jsfGetJSONWithCallback, but handles ONLY functions (and does not print the initial 'function' text) */ void jsfGetJSONForFunctionWithCallback(JsVar *var, JSONFlags flags, vcbprintf_callback user_callback, void *user_data) { assert(jsvIsFunction(var)); JsVar *codeVar = 0; // TODO: this should really be in jsvAsString JsvObjectIterator it; jsvObjectIteratorNew(&it, var); bool firstParm = true; cbprintf(user_callback, user_data, "("); while (jsvObjectIteratorHasValue(&it)) { JsVar *child = jsvObjectIteratorGetKey(&it); if (jsvIsFunctionParameter(child)) { if (firstParm) firstParm=false; else cbprintf(user_callback, user_data, ","); JsVar *name = jsvNewFromStringVar(child, 1, JSVAPPENDSTRINGVAR_MAXLENGTH); cbprintf(user_callback, user_data, "%v", name); jsvUnLock(name); } else if (jsvIsString(child) && jsvIsStringEqual(child, JSPARSE_FUNCTION_CODE_NAME)) { codeVar = jsvObjectIteratorGetValue(&it); } jsvUnLock(child); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); cbprintf(user_callback, user_data, ") "); if (jsvIsNative(var)) { cbprintf(user_callback, user_data, "{ [native code] }"); } else { if (codeVar) { if (flags & JSON_LIMIT) { cbprintf(user_callback, user_data, "{%s}", JSON_LIMIT_TEXT); } else { bool hasNewLine = jsvGetStringIndexOf(codeVar,'\n')>=0; user_callback(hasNewLine?"{\n ":"{", user_data); if (jsvIsFunctionReturn(var)) user_callback("return ", user_data); // reconstruct the tokenised output into something more readable char buf[32]; unsigned char lastch = 0; JsvStringIterator it; jsvStringIteratorNew(&it, codeVar, 0); while (jsvStringIteratorHasChar(&it)) { unsigned char ch = (unsigned char)jsvStringIteratorGetChar(&it); if (jslNeedSpaceBetween(lastch, ch)) user_callback(" ", user_data); jslFunctionCharAsString(ch, buf, sizeof(buf)); user_callback(buf, user_data); jsvStringIteratorNext(&it); lastch = ch; } jsvStringIteratorFree(&it); user_callback(hasNewLine?"\n}":"}", user_data); } } else cbprintf(user_callback, user_data, "{}"); } jsvUnLock(codeVar); }
/*JSON{ "type" : "method", "class" : "String", "name" : "replace", "generate" : "jswrap_string_replace", "params" : [ ["subStr","JsVar","The string to search for"], ["newSubStr","JsVar","The string to replace it with"] ], "return" : ["JsVar","This string with `subStr` replaced"] } Search and replace ONE occurrance of `subStr` with `newSubStr` and return the result. This doesn't alter the original string. Regular expressions not supported. */ JsVar *jswrap_string_replace(JsVar *parent, JsVar *subStr, JsVar *newSubStr) { JsVar *str = jsvAsString(parent); #ifndef SAVE_ON_FLASH // Use RegExp if one is passed in if (jsvIsInstanceOf(subStr, "RegExp")) { JsVar *replace; if (jsvIsFunction(newSubStr) || jsvIsString(newSubStr)) replace = jsvLockAgain(newSubStr); else replace = jsvAsString(newSubStr); jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(0)); bool global = jswrap_regexp_hasFlag(subStr,'g'); JsVar *match; match = jswrap_regexp_exec(subStr, str); while (match && !jsvIsNull(match) && !jspIsInterrupted()) { // get info about match JsVar *matchStr = jsvGetArrayItem(match,0); JsVarInt idx = jsvGetIntegerAndUnLock(jsvObjectGetChild(match,"index",0)); JsVarInt len = (JsVarInt)jsvGetStringLength(matchStr); // do the replacement JsVar *newStr = jsvNewFromStringVar(str, 0, (size_t)idx); JsvStringIterator dst; jsvStringIteratorNew(&dst, newStr, 0); jsvStringIteratorGotoEnd(&dst); if (jsvIsFunction(replace)) { unsigned int argCount = 0; JsVar *args[13]; args[argCount++] = jsvLockAgain(matchStr); JsVar *v; while ((v = jsvGetArrayItem(match, (JsVarInt)argCount))) args[argCount++] = v; args[argCount++] = jsvObjectGetChild(match,"index",0); args[argCount++] = jsvObjectGetChild(match,"input",0); JsVar *result = jsvAsStringAndUnLock(jspeFunctionCall(replace, 0, 0, false, (JsVarInt)argCount, args)); jsvUnLockMany(argCount, args); jsvStringIteratorAppendString(&dst, result, 0); jsvUnLock(result); } else { JsvStringIterator src; jsvStringIteratorNew(&src, replace, 0); while (jsvStringIteratorHasChar(&src)) { char ch = jsvStringIteratorGetChar(&src); if (ch=='$') { jsvStringIteratorNext(&src); ch = jsvStringIteratorGetChar(&src); JsVar *group = 0; if (ch>'0' && ch<='9') group = jsvGetArrayItem(match, ch-'0'); if (group) { jsvStringIteratorAppendString(&dst, group, 0); jsvUnLock(group); } else { jsvStringIteratorAppend(&dst, '$'); jsvStringIteratorAppend(&dst, ch); } } else { jsvStringIteratorAppend(&dst, ch); } jsvStringIteratorNext(&src); } jsvStringIteratorFree(&src); } JsVarInt lastIndex = 1+(JsVarInt)jsvStringIteratorGetIndex(&dst); jsvStringIteratorAppendString(&dst, str, (size_t)(idx+len)); jsvStringIteratorFree(&dst); jsvUnLock2(str,matchStr); str = newStr; // search again if global jsvUnLock(match); match = 0; if (global) { jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(lastIndex)); match = jswrap_regexp_exec(subStr, str); } } jsvUnLock(match); jsvUnLock(replace); // reset lastIndex if global if (global) jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(0)); return str; } #endif newSubStr = jsvAsString(newSubStr); subStr = jsvAsString(subStr); int idx = jswrap_string_indexOf(parent, subStr, 0, false); if (idx>=0) { JsVar *newStr = jsvNewFromStringVar(str, 0, (size_t)idx); jsvAppendStringVar(newStr, newSubStr, 0, JSVAPPENDSTRINGVAR_MAXLENGTH); jsvAppendStringVar(newStr, str, (size_t)idx+jsvGetStringLength(subStr), JSVAPPENDSTRINGVAR_MAXLENGTH); jsvUnLock(str); str = newStr; } jsvUnLock2(subStr, newSubStr); return str; }
/* matchhere: search for regexp at beginning of text. Only handles up to '|' character. Modifies txtIt */ static JsVar *matchhere(char *regexp, JsvStringIterator *txtIt, matchInfo info) { if (jspIsInterrupted()) return 0; if (regexp[0] == '\0' || // end of regex regexp[0] == '|') // end of this 'or' section of regex return matchfound(txtIt, info); if (regexp[0] == '^') { // must be beginning of String if (jsvStringIteratorGetIndex(txtIt)!=0) return 0; // no match if (!jspCheckStackPosition()) return 0; return matchhere(regexp+1, txtIt, info); } // Marker for end of String if (regexp[0] == '$') { if (!jsvStringIteratorHasChar(txtIt)) return matchhere(regexp+1, txtIt, info); else return nomatchfound(regexp+1, info); // not the end, it's a fail } if (regexp[0] == '(') { info.groupStart[info.groups] = jsvStringIteratorGetIndex(txtIt); info.groupEnd[info.groups] = info.groupStart[info.groups]; if (info.groups<MAX_GROUPS) info.groups++; if (!jspCheckStackPosition()) return 0; return matchhere(regexp+1, txtIt, info); } if (regexp[0] == ')') { if (info.groups>0) info.groupEnd[info.groups-1] = jsvStringIteratorGetIndex(txtIt); if (!jspCheckStackPosition()) return 0; return matchhere(regexp+1, txtIt, info); } int charLength; bool charMatched = matchcharacter(regexp, txtIt, &charLength, &info); if (regexp[charLength] == '*' || regexp[charLength] == '+') { char op = regexp[charLength]; if (!charMatched && op=='+') { // with '+' operator it has to match at least once return nomatchfound(®exp[charLength+1], info); } char *regexpAfterStar = regexp+charLength+1; JsvStringIterator txtIt2; // Try and match everything after right now txtIt2 = jsvStringIteratorClone(txtIt); JsVar *lastrmatch = matchhere(regexpAfterStar, &txtIt2, info); jsvStringIteratorFree(&txtIt2); // Otherwise try and match more than one while (jsvStringIteratorHasChar(txtIt) && charMatched) { // We had this character matched, so move on and see if we can match with the new one jsvStringIteratorNext(txtIt); charMatched = matchcharacter(regexp, txtIt, &charLength, &info); // See if we can match after the character... txtIt2 = jsvStringIteratorClone(txtIt); JsVar *rmatch = matchhere(regexpAfterStar, &txtIt2, info); jsvStringIteratorFree(&txtIt2); // can't match with this - use the last one if (rmatch) { jsvUnLock(lastrmatch); lastrmatch = rmatch; } } return lastrmatch; } // This character is matched if (jsvStringIteratorHasChar(txtIt) && charMatched) { jsvStringIteratorNext(txtIt); if (!jspCheckStackPosition()) return 0; return matchhere(regexp+charLength, txtIt, info); } // No match return nomatchfound(®exp[charLength], info); }
/*JSON{ "type" : "staticmethod", "class" : "url", "name" : "parse", "generate" : "jswrap_url_parse", "params" : [ ["urlStr","JsVar","A URL to be parsed"], ["parseQuery","bool","Whether to parse the query string into an object not (default = false)"] ], "return" : ["JsVar","An object containing options for ```http.request``` or ```http.get```. Contains `method`, `host`, `path`, `pathname`, `search`, `port` and `query`"] } A utility function to split a URL into parts This is useful in web servers for instance when handling a request. For instance `url.parse("/a?b=c&d=e",true)` returns `{"method":"GET","host":"","path":"/a?b=c&d=e","pathname":"/a","search":"?b=c&d=e","port":80,"query":{"b":"c","d":"e"}}` */ JsVar *jswrap_url_parse(JsVar *url, bool parseQuery) { if (!jsvIsString(url)) return 0; JsVar *obj = jsvNewWithFlags(JSV_OBJECT); if (!obj) return 0; // out of memory // scan string to try and pick stuff out JsvStringIterator it; jsvStringIteratorNew(&it, url, 0); int slashes = 0; int colons = 0; int addrStart = -1; int portStart = -1; int pathStart = -1; int searchStart = -1; int charIdx = 0; int portNumber = 0; while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); if (ch == '/') { slashes++; if (pathStart<0) pathStart = charIdx; if (colons==1 && slashes==2 && addrStart<0) { addrStart = charIdx; pathStart = -1; searchStart = -1; } } if (ch == ':') { colons++; if (addrStart>=0 && pathStart<0) portStart = charIdx; } if (portStart>=0 && charIdx>portStart && pathStart<0 && ch >= '0' && ch <= '9') { portNumber = portNumber*10 + (ch-'0'); } if (ch == '?' && pathStart>=0) { searchStart = charIdx; } jsvStringIteratorNext(&it); charIdx++; } jsvStringIteratorFree(&it); // try and sort stuff out if (pathStart<0) pathStart = charIdx; if (pathStart<0) pathStart = charIdx; int addrEnd = (portStart>=0) ? portStart : pathStart; // pull out details if (addrStart>0) jsvObjectSetChildAndUnLock(obj, "protocol", jsvNewFromStringVar(url, 0, (size_t)addrStart-1)); jsvObjectSetChildAndUnLock(obj, "method", jsvNewFromString("GET")); jsvObjectSetChildAndUnLock(obj, "host", jsvNewFromStringVar(url, (size_t)(addrStart+1), (size_t)(addrEnd-(addrStart+1)))); JsVar *v; v = jsvNewFromStringVar(url, (size_t)pathStart, JSVAPPENDSTRINGVAR_MAXLENGTH); if (jsvGetStringLength(v)==0) jsvAppendString(v, "/"); jsvObjectSetChildAndUnLock(obj, "path", v); v = jsvNewFromStringVar(url, (size_t)pathStart, (size_t)((searchStart>=0)?(searchStart-pathStart):JSVAPPENDSTRINGVAR_MAXLENGTH)); if (jsvGetStringLength(v)==0) jsvAppendString(v, "/"); jsvObjectSetChildAndUnLock(obj, "pathname", v); jsvObjectSetChildAndUnLock(obj, "search", (searchStart>=0)?jsvNewFromStringVar(url, (size_t)searchStart, JSVAPPENDSTRINGVAR_MAXLENGTH):jsvNewNull()); jsvObjectSetChildAndUnLock(obj, "port", (portNumber<=0 || portNumber>65535) ? jsvNewWithFlags(JSV_NULL) : jsvNewFromInteger(portNumber)); JsVar *query = (searchStart>=0)?jsvNewFromStringVar(url, (size_t)(searchStart+1), JSVAPPENDSTRINGVAR_MAXLENGTH):jsvNewNull(); if (parseQuery && !jsvIsNull(query)) { JsVar *queryStr = query; jsvStringIteratorNew(&it, query, 0); query = jsvNewWithFlags(JSV_OBJECT); JsVar *key = jsvNewFromEmptyString(); JsVar *val = jsvNewFromEmptyString(); bool hadEquals = false; while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); if (ch=='&') { if (jsvGetStringLength(key)>0 || jsvGetStringLength(val)>0) { key = jsvAsArrayIndexAndUnLock(key); // make sure "0" gets made into 0 jsvMakeIntoVariableName(key, val); jsvAddName(query, key); jsvUnLock2(key, val); key = jsvNewFromEmptyString(); val = jsvNewFromEmptyString(); hadEquals = false; } } else if (!hadEquals && ch=='=') { hadEquals = true; } else { // decode percent escape chars if (ch=='%') { jsvStringIteratorNext(&it); ch = jsvStringIteratorGetChar(&it); jsvStringIteratorNext(&it); ch = (char)((chtod(ch)<<4) | chtod(jsvStringIteratorGetChar(&it))); } if (hadEquals) jsvAppendCharacter(val, ch); else jsvAppendCharacter(key, ch); } jsvStringIteratorNext(&it); charIdx++; } jsvStringIteratorFree(&it); jsvUnLock(queryStr); if (jsvGetStringLength(key)>0 || jsvGetStringLength(val)>0) { key = jsvAsArrayIndexAndUnLock(key); // make sure "0" gets made into 0 jsvMakeIntoVariableName(key, val); jsvAddName(query, key); } jsvUnLock2(key, val); } jsvObjectSetChildAndUnLock(obj, "query", query); return obj; }
JsVar *wrap_fat_readdir(JsVar *path) { JsVar *arr = 0; // undefined unless we can open card char pathStr[JS_DIR_BUF_SIZE] = ""; if (!jsvIsUndefined(path)) jsvGetString(path, pathStr, JS_DIR_BUF_SIZE); #ifdef LINUX if (!pathStr[0]) strcpy(pathStr, "."); // deal with empty readdir #endif FRESULT res = 0; if (jsfsInit()) { #ifndef LINUX DIR dirs; if ((res=f_opendir(&dirs, pathStr)) == FR_OK) { char lfnBuf[_MAX_LFN+1]; FILINFO Finfo; Finfo.lfname = lfnBuf; Finfo.lfsize = sizeof(lfnBuf); #else DIR *dir = opendir(pathStr); if(dir) { #endif arr = jsvNewWithFlags(JSV_ARRAY); if (arr) { // could be out of memory #ifndef LINUX while (((res=f_readdir(&dirs, &Finfo)) == FR_OK) && Finfo.fname[0]) { char *fn = GET_FILENAME(Finfo); #else struct dirent *pDir=NULL; while((pDir = readdir(dir)) != NULL) { char *fn = (*pDir).d_name; #endif JsVar *fnVar = jsvNewFromString(fn); if (fnVar) // out of memory? jsvArrayPush(arr, fnVar); } } #ifdef LINUX closedir(dir); #endif } } if (res) jsfsReportError("Unable to list files", res); return arr; } /*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "writeFile", "generate" : "wrap_fat_writeFile", "description" : [ "Write the data to the given file", "NOTE: Espruino does not yet support Async file IO, so this function behaves like the 'Sync' version." ], "params" : [ [ "path", "JsVar", "The path of the file to write" ], [ "data", "JsVar", "The data to write to the file" ] ] }*/ /*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "writeFileSync", "ifndef" : "SAVE_ON_FLASH", "generate" : "wrap_fat_writeFile", "description" : [ "Write the data to the given file" ], "params" : [ [ "path", "JsVar", "The path of the file to write" ], [ "data", "JsVar", "The data to write to the file" ] ] }*/ /*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "appendFile", "generate" : "wrap_fat_appendFile", "description" : [ "Append the data to the given file, created a new file if it doesn't exist", "NOTE: Espruino does not yet support Async file IO, so this function behaves like the 'Sync' version." ], "params" : [ [ "path", "JsVar", "The path of the file to write" ], [ "data", "JsVar", "The data to write to the file" ] ] }*/ /*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "appendFileSync", "ifndef" : "SAVE_ON_FLASH", "generate" : "wrap_fat_appendFile", "description" : [ "Append the data to the given file, created a new file if it doesn't exist" ], "params" : [ [ "path", "JsVar", "The path of the file to write" ], [ "data", "JsVar", "The data to write to the file" ] ] }*/ void wrap_fat_writeOrAppendFile(JsVar *path, JsVar *data, bool append) { char pathStr[JS_DIR_BUF_SIZE] = ""; if (!jsvIsUndefined(path)) jsvGetString(path, pathStr, JS_DIR_BUF_SIZE); FRESULT res = 0; if (jsfsInit()) { #ifndef LINUX FIL file; if ((res=f_open(&file, pathStr, FA_WRITE|(append ? FA_OPEN_ALWAYS : FA_CREATE_ALWAYS))) == FR_OK) { if (append) { // move to end of file to append data f_lseek(&file, file.fsize); // if (res != FR_OK) jsfsReportError("Unable to move to end of file", res); } #else FILE *file = fopen(pathStr, append?"a":"w"); if (file) { #endif JsvStringIterator it; JsVar *dataString = jsvAsString(data, false); jsvStringIteratorNew(&it, dataString, 0); size_t toWrite = 0; size_t written = 0; while (jsvStringIteratorHasChar(&it) && res==FR_OK && written==toWrite) { // re-use pathStr buffer toWrite = 0; while (jsvStringIteratorHasChar(&it) && toWrite < JS_DIR_BUF_SIZE) { pathStr[toWrite++] = jsvStringIteratorGetChar(&it); jsvStringIteratorNext(&it); } #ifndef LINUX res = f_write(&file, pathStr, toWrite, &written); #else written = fwrite(pathStr, 1, toWrite, file); #endif } jsvStringIteratorFree(&it); jsvUnLock(dataString); #ifndef LINUX f_close(&file); #else fclose(file); #endif } } if (res) jsfsReportError("Unable to write file", res); } void wrap_fat_writeFile(JsVar *path, JsVar *data) { wrap_fat_writeOrAppendFile(path, data, false); } void wrap_fat_appendFile(JsVar *path, JsVar *data) { wrap_fat_writeOrAppendFile(path, data, true); }
bool httpParseHeaders(JsVar **receiveData, JsVar *objectForData, bool isServer) { // find /r/n/r/n int newlineIdx = 0; int strIdx = 0; int headerEnd = -1; JsvStringIterator it; jsvStringIteratorNew(&it, *receiveData, 0); while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); if (ch == '\r') { if (newlineIdx==0) newlineIdx=1; else if (newlineIdx==2) newlineIdx=3; } else if (ch == '\n') { if (newlineIdx==1) newlineIdx=2; else if (newlineIdx==3) { headerEnd = strIdx+1; } } else newlineIdx=0; jsvStringIteratorNext(&it); strIdx++; } jsvStringIteratorFree(&it); // skip if we have no header if (headerEnd<0) return false; // Now parse the header JsVar *vHeaders = jsvNewWithFlags(JSV_OBJECT); if (!vHeaders) return true; jsvUnLock(jsvAddNamedChild(objectForData, vHeaders, "headers")); strIdx = 0; int firstSpace = -1; int secondSpace = -1; int lineNumber = 0; int lastLineStart = 0; int colonPos = 0; //jsiConsolePrintStringVar(receiveData); jsvStringIteratorNew(&it, *receiveData, 0); while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); if (ch==' ' || ch=='\r') { if (firstSpace<0) firstSpace = strIdx; else if (secondSpace<0) secondSpace = strIdx; } if (ch == ':' && colonPos<0) colonPos = strIdx; if (ch == '\r') { if (lineNumber>0 && colonPos>lastLineStart && lastLineStart<strIdx) { JsVar *hVal = jsvNewFromEmptyString(); if (hVal) jsvAppendStringVar(hVal, *receiveData, colonPos+2, strIdx-(colonPos+2)); JsVar *hKey = jsvNewFromEmptyString(); if (hKey) { jsvMakeIntoVariableName(hKey, hVal); jsvAppendStringVar(hKey, *receiveData, lastLineStart, colonPos-lastLineStart); jsvAddName(vHeaders, hKey); jsvUnLock(hKey); } jsvUnLock(hVal); } lineNumber++; colonPos=-1; } if (ch == '\r' || ch == '\n') { lastLineStart = strIdx+1; } jsvStringIteratorNext(&it); strIdx++; } jsvStringIteratorFree(&it); jsvUnLock(vHeaders); // try and pull out methods/etc if (isServer) { JsVar *vMethod = jsvNewFromEmptyString(); if (vMethod) { jsvAppendStringVar(vMethod, *receiveData, 0, firstSpace); jsvUnLock(jsvAddNamedChild(objectForData, vMethod, "method")); jsvUnLock(vMethod); } JsVar *vUrl = jsvNewFromEmptyString(); if (vUrl) { jsvAppendStringVar(vUrl, *receiveData, firstSpace+1, secondSpace-(firstSpace+1)); jsvUnLock(jsvAddNamedChild(objectForData, vUrl, "url")); jsvUnLock(vUrl); } } // strip out the header JsVar *afterHeaders = jsvNewFromEmptyString(); if (!afterHeaders) return true; jsvAppendStringVar(afterHeaders, *receiveData, headerEnd, JSVAPPENDSTRINGVAR_MAXLENGTH); jsvUnLock(*receiveData); *receiveData = afterHeaders; return true; }
void jsvStringIteratorSetChar(JsvStringIterator *it, char c) { if (jsvStringIteratorHasChar(it)) it->ptr[it->charIdx] = c; }
void jsvStringIteratorSetCharAndNext(JsvStringIterator *it, char c) { if (jsvStringIteratorHasChar(it)) it->ptr[it->charIdx] = c; jsvStringIteratorNextInline(it); }
/** * \brief Iterate over the contents of the content of a variable, calling callback for each. * Contents may be: * * numeric -> output * * a string -> output each character * * array/arraybuffer -> call itself on each element * object -> call itself object.count times, on object.data */ bool jsvIterateCallback( JsVar *data, // The data to iterate over. void (*callback)(int item, void *callbackData), // The callback function invoke. void *callbackData // Data to be passed to the callback function ) { bool ok = true; // Handle the data being a single numeric. if (jsvIsNumeric(data)) { callback((int)jsvGetInteger(data), callbackData); } // Handle the data being an object. else if (jsvIsObject(data)) { JsVar *countVar = jsvObjectGetChild(data, "count", 0); JsVar *dataVar = jsvObjectGetChild(data, "data", 0); if (countVar && dataVar && jsvIsNumeric(countVar)) { int n = (int)jsvGetInteger(countVar); while (ok && n-- > 0) { ok = jsvIterateCallback(dataVar, callback, callbackData); } } else { jsWarn("If specifying an object, it must be of the form {data : ..., count : N}"); } jsvUnLock2(countVar, dataVar); } // Handle the data being a string else if (jsvIsString(data)) { JsvStringIterator it; jsvStringIteratorNew(&it, data, 0); while (jsvStringIteratorHasChar(&it) && ok) { char ch = jsvStringIteratorGetChar(&it); callback(ch, callbackData); jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); } // Handle the data being an array buffer else if (jsvIsArrayBuffer(data)) { JsvArrayBufferIterator it; jsvArrayBufferIteratorNew(&it, data, 0); if (JSV_ARRAYBUFFER_GET_SIZE(it.type) == 1 && !JSV_ARRAYBUFFER_IS_SIGNED(it.type)) { // faster for single byte arrays. while (jsvArrayBufferIteratorHasElement(&it)) { callback((int)(unsigned char)jsvStringIteratorGetChar(&it.it), callbackData); jsvArrayBufferIteratorNext(&it); } } else { while (jsvArrayBufferIteratorHasElement(&it)) { callback((int)jsvArrayBufferIteratorGetIntegerValue(&it), callbackData); jsvArrayBufferIteratorNext(&it); } } jsvArrayBufferIteratorFree(&it); } // Handle the data being iterable else if (jsvIsIterable(data)) { JsvIterator it; jsvIteratorNew(&it, data); while (jsvIteratorHasElement(&it) && ok) { JsVar *el = jsvIteratorGetValue(&it); ok = jsvIterateCallback(el, callback, callbackData); jsvUnLock(el); jsvIteratorNext(&it); } jsvIteratorFree(&it); } else { jsWarn("Expecting a number or something iterable, got %t", data); ok = false; } return ok; }
JsVar *jswrap_spi_send(JsVar *parent, JsVar *srcdata, Pin nss_pin) { NOT_USED(parent); IOEventFlags device = jsiGetDeviceFromClass(parent); jswrap_spi_send_data data; if (!jsspiGetSendFunction(parent, &data.spiSend, &data.spiSendData)) return 0; JsVar *dst = 0; // we're sending and receiving if (DEVICE_IS_SPI(device)) jshSPISetReceive(device, true); // assert NSS if (nss_pin!=PIN_UNDEFINED) jshPinOutput(nss_pin, false); // send data if (jsvIsNumeric(srcdata)) { int r = data.spiSend((unsigned char)jsvGetInteger(srcdata), &data.spiSendData); if (r<0) r = data.spiSend(-1, &data.spiSendData); dst = jsvNewFromInteger(r); // retrieve the byte (no send!) } else if (jsvIsString(srcdata)) { dst = jsvNewFromEmptyString(); JsvStringIterator it; jsvStringIteratorNew(&it, srcdata, 0); int incount = 0, outcount = 0; while (jsvStringIteratorHasChar(&it) && !jspIsInterrupted()) { unsigned char in = (unsigned char)jsvStringIteratorGetChar(&it); incount++; int out = data.spiSend(in, &data.spiSendData); if (out>=0) { outcount++; char outc = (char)out; jsvAppendStringBuf(dst, (char*)&outc, 1); } jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); // finally add the remaining bytes (no send!) while (outcount < incount && !jspIsInterrupted()) { outcount++; unsigned char out = (unsigned char)data.spiSend(-1, &data.spiSendData); jsvAppendStringBuf(dst, (char*)&out, 1); } } else { int nBytes = jsvIterateCallbackCount(srcdata); dst = jsvNewTypedArray(ARRAYBUFFERVIEW_UINT8, nBytes); if (dst) { data.rxAmt = data.txAmt = 0; jsvArrayBufferIteratorNew(&data.it, dst, 0); // Write data jsvIterateCallback(srcdata, (void (*)(int, void *))jswrap_spi_send_cb, &data); // Wait until SPI send is finished, and flush data while (data.rxAmt < data.txAmt && !jspIsInterrupted()) jswrap_spi_send_cb(-1, &data); jsvArrayBufferIteratorFree(&data.it); } } // de-assert NSS if (nss_pin!=PIN_UNDEFINED) jshPinOutput(nss_pin, true); return dst; }
/** * Send data through SPI. * The data can be in a variety of formats including: * * `numeric` - A single byte is transmitted. * * `string` - Each character in the string is transmitted. * * `iterable` - An iterable object is transmitted. * \return the Received bytes (MISO). This is byte array. */ JsVar *jswrap_spi_send( JsVar *parent, //!< A description of the SPI device to send data through. JsVar *srcdata, //!< The data to send through SPI. Pin nss_pin //!< The pin to toggle low then high (CS) ) { // Debug // jsiConsolePrintf("jswrap_spi_send called: parent=%j, srcdata=%j, nss_pin=%p\n", parent, srcdata, nss_pin); NOT_USED(parent); IOEventFlags device = jsiGetDeviceFromClass(parent); jswrap_spi_send_data data; if (!jsspiGetSendFunction(parent, &data.spiSend, &data.spiSendData)) return 0; JsVar *dst = 0; // we're sending and receiving if (DEVICE_IS_SPI(device)) jshSPISetReceive(device, true); // assert NSS if (nss_pin!=PIN_UNDEFINED) jshPinOutput(nss_pin, false); // Now that we are setup, we can send the data. // Handle the data being a single byte value if (jsvIsNumeric(srcdata)) { int r = data.spiSend((unsigned char)jsvGetInteger(srcdata), &data.spiSendData); if (r<0) r = data.spiSend(-1, &data.spiSendData); dst = jsvNewFromInteger(r); // retrieve the byte (no send!) } // Handle the data being a string else if (jsvIsString(srcdata)) { dst = jsvNewFromEmptyString(); JsvStringIterator it; jsvStringIteratorNew(&it, srcdata, 0); int incount = 0, outcount = 0; while (jsvStringIteratorHasChar(&it) && !jspIsInterrupted()) { unsigned char in = (unsigned char)jsvStringIteratorGetChar(&it); incount++; int out = data.spiSend(in, &data.spiSendData); if (out>=0) { outcount++; char outc = (char)out; jsvAppendStringBuf(dst, (char*)&outc, 1); } jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); // finally add the remaining bytes (no send!) while (outcount < incount && !jspIsInterrupted()) { outcount++; unsigned char out = (unsigned char)data.spiSend(-1, &data.spiSendData); jsvAppendStringBuf(dst, (char*)&out, 1); } } // Handle the data being an iterable. else { int nBytes = jsvIterateCallbackCount(srcdata); dst = jsvNewTypedArray(ARRAYBUFFERVIEW_UINT8, nBytes); if (dst) { data.rxAmt = data.txAmt = 0; jsvArrayBufferIteratorNew(&data.it, dst, 0); // Write data jsvIterateCallback(srcdata, (void (*)(int, void *))jswrap_spi_send_cb, &data); // Wait until SPI send is finished, and flush data while (data.rxAmt < data.txAmt && !jspIsInterrupted()) jswrap_spi_send_cb(-1, &data); jsvArrayBufferIteratorFree(&data.it); } } // de-assert NSS if (nss_pin!=PIN_UNDEFINED) jshPinOutput(nss_pin, true); return dst; }
/** Iterate over the contents of the content of a variable, calling callback for each. Contents may be: * numeric -> output * a string -> output each character * array/arraybuffer -> call itself on each element * {data:..., count:...} -> call itself object.count times, on object.data * {callback:...} -> call the given function, call itself on return value */ bool jsvIterateCallback( JsVar *data, jsvIterateCallbackFn callback, void *callbackData ) { bool ok = true; // Handle the data being a single numeric. if (jsvIsNumeric(data)) { callback((int)jsvGetInteger(data), callbackData); } // Handle the data being an object. else if (jsvIsObject(data)) { JsVar *callbackVar = jsvObjectGetChild(data, "callback", 0); if (jsvIsFunction(callbackVar)) { JsVar *result = jspExecuteFunction(callbackVar,0,0,NULL); jsvUnLock(callbackVar); if (result) { bool r = jsvIterateCallback(result, callback, callbackData); jsvUnLock(result); return r; } return true; } jsvUnLock(callbackVar); JsVar *countVar = jsvObjectGetChild(data, "count", 0); JsVar *dataVar = jsvObjectGetChild(data, "data", 0); if (countVar && dataVar && jsvIsNumeric(countVar)) { int n = (int)jsvGetInteger(countVar); while (ok && n-- > 0) { ok = jsvIterateCallback(dataVar, callback, callbackData); } } else { jsExceptionHere(JSET_TYPEERROR, "If specifying an object, it must be of the form {data : ..., count : N} or {callback : fn} - got %j", data); ok = false; } jsvUnLock2(countVar, dataVar); } // Handle the data being a string else if (jsvIsString(data)) { JsvStringIterator it; jsvStringIteratorNew(&it, data, 0); while (jsvStringIteratorHasChar(&it) && ok) { char ch = jsvStringIteratorGetChar(&it); callback(ch, callbackData); jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); } // Handle the data being an array buffer else if (jsvIsArrayBuffer(data)) { JsvArrayBufferIterator it; jsvArrayBufferIteratorNew(&it, data, 0); if (JSV_ARRAYBUFFER_GET_SIZE(it.type) == 1 && !JSV_ARRAYBUFFER_IS_SIGNED(it.type)) { JsvStringIterator *sit = &it.it; // faster for single byte arrays - read using the string iterator. while (jsvStringIteratorHasChar(sit)) { callback((int)(unsigned char)jsvStringIteratorGetChar(sit), callbackData); jsvStringIteratorNextInline(sit); } } else { while (jsvArrayBufferIteratorHasElement(&it)) { callback((int)jsvArrayBufferIteratorGetIntegerValue(&it), callbackData); jsvArrayBufferIteratorNext(&it); } } jsvArrayBufferIteratorFree(&it); } // Handle the data being iterable else if (jsvIsIterable(data)) { JsvIterator it; jsvIteratorNew(&it, data, JSIF_EVERY_ARRAY_ELEMENT); while (jsvIteratorHasElement(&it) && ok) { JsVar *el = jsvIteratorGetValue(&it); ok = jsvIterateCallback(el, callback, callbackData); jsvUnLock(el); jsvIteratorNext(&it); } jsvIteratorFree(&it); } else { jsExceptionHere(JSET_TYPEERROR, "Expecting a number or something iterable, got %t", data); ok = false; } return ok; }