There are no doc comments for this declaration.
";
+ }
+ domTldDocs.classList.remove("hidden");
+ }
+
+ function typeIsErrSet(typeIndex) {
+ let typeObj = getType(typeIndex);
+ return typeObj.kind === typeKinds.ErrorSet;
+ }
+
+ function typeIsStructWithNoFields(typeIndex) {
+ let typeObj = getType(typeIndex);
+ if (typeObj.kind !== typeKinds.Struct) return false;
+ return typeObj.field_types.length == 0;
+ }
+
+ function typeIsGenericFn(typeIndex) {
+ let typeObj = getType(typeIndex);
+ if (typeObj.kind !== typeKinds.Fn) {
+ return false;
+ }
+ return typeObj.generic_ret != null;
+ }
+
+ function renderFn(fnDecl) {
+ if ("refPath" in fnDecl.value.expr) {
+ let last = fnDecl.value.expr.refPath.length - 1;
+ let lastExpr = fnDecl.value.expr.refPath[last];
+ console.assert("declRef" in lastExpr);
+ fnDecl = getDecl(lastExpr.declRef);
+ }
+
+ let value = resolveValue(fnDecl.value);
+ console.assert("type" in value.expr);
+ let typeObj = getType(value.expr.type);
+
+ domFnProtoCode.innerHTML = renderTokens(ex(value.expr, { fnDecl: fnDecl }));
+ domFnSourceLink.classList.remove("hidden");
+ domFnSourceLink.innerHTML = "[" + short + "
" + "
" + long + "
";
+ }
+ else {
+ tdDesc.innerHTML = markdown(short, container);
+ }
+ } else {
+ tdDesc.innerHTML = "
No documentation provided.
";
+ }
+ }
+ domSectFns.classList.remove("hidden");
+ }
+
+ let containerNode = getAstNode(container.src);
+ if (containerNode.fields && containerNode.fields.length > 0) {
+ resizeDomList(domListFields, containerNode.fields.length, "
");
+
+ for (let i = 0; i < containerNode.fields.length; i += 1) {
+ let fieldNode = getAstNode(containerNode.fields[i]);
+ let divDom = domListFields.children[i];
+ let fieldName = fieldNode.name;
+ let docs = fieldNode.docs;
+ let docsNonEmpty = docs != null && docs !== "";
+ let extraPreClass = docsNonEmpty ? " fieldHasDocs" : "";
+
+ let html =
+ '
";
+
+ if (docsNonEmpty) {
+ html += '
' + markdown(docs) + "
";
+ }
+ divDom.innerHTML = html;
+ }
+ domSectFields.classList.remove("hidden");
+ }
+
+ if (varsList.length !== 0) {
+ resizeDomList(
+ domListGlobalVars,
+ varsList.length,
+ '
| | |
'
+ );
+ for (let i = 0; i < varsList.length; i += 1) {
+ let decl = varsList[i];
+ let trDom = domListGlobalVars.children[i];
+
+ let tdName = trDom.children[0];
+ let tdNameA = tdName.children[0];
+ let tdType = trDom.children[1];
+ let preType = tdType.children[0];
+ let tdDesc = trDom.children[2];
+
+ tdNameA.setAttribute("href", navLinkDecl(decl.name));
+ tdNameA.textContent = decl.name;
+
+ preType.innerHTML = renderTokens(ex(walkResultTypeRef(decl.value), {}));
+
+ let docs = getAstNode(decl.src).docs;
+ if (docs != null) {
+ tdDesc.innerHTML = shortDescMarkdown(docs);
+ } else {
+ tdDesc.textContent = "";
+ }
+ }
+ domSectGlobalVars.classList.remove("hidden");
+ }
+
+ if (valsList.length !== 0) {
+ resizeDomList(
+ domListValues,
+ valsList.length,
+ '
| | |
'
+ );
+ for (let i = 0; i < valsList.length; i += 1) {
+ let decl = valsList[i];
+ let trDom = domListValues.children[i];
+
+ let tdName = trDom.children[0];
+ let tdNameA = tdName.children[0];
+ let tdType = trDom.children[1];
+ let preType = tdType.children[0];
+ let tdDesc = trDom.children[2];
+
+ tdNameA.setAttribute("href", navLinkDecl(decl.name));
+ tdNameA.textContent = decl.name;
+
+ preType.innerHTML = renderTokens(ex(walkResultTypeRef(decl.value), {}));
+
+ let docs = getAstNode(decl.src).docs;
+ if (docs != null) {
+ tdDesc.innerHTML = shortDescMarkdown(docs);
+ } else {
+ tdDesc.textContent = "";
+ }
+ }
+ domSectValues.classList.remove("hidden");
+ }
+
+ if (testsList.length !== 0) {
+ resizeDomList(
+ domListTests,
+ testsList.length,
+ '
| | |
'
+ );
+ for (let i = 0; i < testsList.length; i += 1) {
+ let decl = testsList[i];
+ let trDom = domListTests.children[i];
+
+ let tdName = trDom.children[0];
+ let tdNamePre = tdName.children[0];
+ let tdType = trDom.children[1];
+ let tdTypePre = tdType.children[0];
+ let tdDesc = trDom.children[2];
+
+ tdNamePre.innerHTML = renderSingleToken(Tok.identifier(decl.name));
+
+ tdTypePre.innerHTML = ex(walkResultTypeRef(decl.value), {});
+
+ let docs = getAstNode(decl.src).docs;
+ if (docs != null) {
+ tdDesc.innerHTML = shortDescMarkdown(docs);
+ } else {
+ tdDesc.textContent = "";
+ }
+ }
+ domSectTests.classList.remove("hidden");
+ }
+
+ if (container.kind !== typeKinds.Struct || containerNode.fields.length > 0) {
+ domHdrName.innerHTML = "
" +
+ zigAnalysis.typeKinds[container.kind] +
+ "
";
+ domHdrName.classList.remove("hidden");
+ }
+ }
+
+ function operatorCompare(a, b) {
+ if (a === b) {
+ return 0;
+ } else if (a < b) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+
+ function detectRootIsStd() {
+ let rootMod = zigAnalysis.modules[zigAnalysis.rootMod];
+ if (rootMod.table["std"] == null) {
+ // no std mapped into the root module
+ return false;
+ }
+ let stdMod = zigAnalysis.modules[rootMod.table["std"]];
+ if (stdMod == null) return false;
+ return rootMod.file === stdMod.file;
+ }
+
+ function indexTypeKinds() {
+ let map = {};
+ for (let i = 0; i < zigAnalysis.typeKinds.length; i += 1) {
+ map[zigAnalysis.typeKinds[i]] = i;
+ }
+ // This is just for debugging purposes, not needed to function
+ let assertList = [
+ "Type",
+ "Void",
+ "Bool",
+ "NoReturn",
+ "Int",
+ "Float",
+ "Pointer",
+ "Array",
+ "Struct",
+ "ComptimeFloat",
+ "ComptimeInt",
+ "Undefined",
+ "Null",
+ "Optional",
+ "ErrorUnion",
+ "ErrorSet",
+ "Enum",
+ "Union",
+ "Fn",
+ "Opaque",
+ "Frame",
+ "AnyFrame",
+ "Vector",
+ "EnumLiteral",
+ ];
+ for (let i = 0; i < assertList.length; i += 1) {
+ if (map[assertList[i]] == null)
+ throw new Error("No type kind '" + assertList[i] + "' found");
+ }
+ return map;
+ }
+
+ function findTypeTypeId() {
+ for (let i = 0; i < zigAnalysis.types.length; i += 1) {
+ if (getType(i).kind == typeKinds.Type) {
+ return i;
+ }
+ }
+ throw new Error("No type 'type' found");
+ }
+
+
+ function updateCurNav() {
+ curNav = {
+ hash: location.hash,
+ mode: NAV_MODES.API,
+ modNames: [],
+ modObjs: [],
+ declNames: [],
+ declObjs: [],
+ callName: null,
+ activeGuide: null,
+ activeGuideScrollTo: null,
+ };
+ curNavSearch = "";
+
+ const mode = location.hash.substring(0, 3);
+ let query = location.hash.substring(3);
+
+ let qpos = query.indexOf("?");
+ let nonSearchPart;
+ if (qpos === -1) {
+ nonSearchPart = query;
+ } else {
+ nonSearchPart = query.substring(0, qpos);
+ curNavSearch = decodeURIComponent(query.substring(qpos + 1));
+ }
+
+ const DEFAULT_HASH = NAV_MODES.API + zigAnalysis.modules[zigAnalysis.rootMod].name;
+ switch (mode) {
+ case NAV_MODES.API:
+ // #A;MODULE:decl.decl.decl?search-term
+ curNav.mode = mode;
+ {
+ let parts = nonSearchPart.split(":");
+ if (parts[0] == "") {
+ location.hash = DEFAULT_HASH;
+ } else {
+ curNav.modNames = decodeURIComponent(parts[0]).split(".");
+ }
+
+ if (parts[1] != null) {
+ curNav.declNames = decodeURIComponent(parts[1]).split(".");
+ }
+ }
+ return;
+ case NAV_MODES.GUIDES:
+ curNav.mode = mode;
+
+ {
+ let parts = nonSearchPart.split(":");
+ curNav.activeGuide = parts[0];
+ if (parts[1] != null) {
+ curNav.activeGuideScrollTo = decodeURIComponent(":" + parts[1]);
+ }
+ }
+ return;
+ default:
+ location.hash = DEFAULT_HASH;
+ return;
+ }
+ }
+
+ function onHashChange(ev) {
+ scrollHistory[curNav.hash] = scrollMonitor.map(function (x) {
+ return [x, x.scrollTop]
+ });
+
+ if (skipNextHashChange == decodeURIComponent(location.hash)) {
+ skipNextHashChange = null;
+ return;
+ }
+ skipNextHashChange = null;
+ updateCurNav();
+
+ if (domSearch.value !== curNavSearch) {
+ domSearch.value = curNavSearch;
+ if (domSearch.value.length == 0)
+ domSearchPlaceholder.classList.remove("hidden");
+ else
+ domSearchPlaceholder.classList.add("hidden");
+ }
+ render();
+ if (imFeelingLucky) {
+ imFeelingLucky = false;
+ activateSelectedResult();
+ }
+
+ scroll();
+ }
+
+ function scroll() {
+ const cur = scrollHistory[location.hash];
+ if (cur) {
+ for (let [elem, offset] of cur) {
+ elem.scrollTo(0, offset);
+ }
+ } else {
+ if (curNav.activeGuideScrollTo) return;
+ for (let elem of scrollMonitor) {
+ elem.scrollTo(0, 0);
+ }
+ }
+ }
+
+ function findSubDecl(parentTypeOrDecl, childName) {
+ let parentType = parentTypeOrDecl;
+ {
+ // Generic functions / resolving decls
+ if ("value" in parentType) {
+ const rv = resolveValue(parentType.value);
+ if ("type" in rv.expr) {
+ const t = getType(rv.expr.type);
+ parentType = t;
+ if (t.kind == typeKinds.Fn && t.generic_ret != null) {
+ let resolvedGenericRet = resolveValue({ expr: t.generic_ret });
+
+ if ("call" in resolvedGenericRet.expr) {
+ let call = zigAnalysis.calls[resolvedGenericRet.expr.call];
+ let resolvedFunc = resolveValue({ expr: call.func });
+ if (!("type" in resolvedFunc.expr)) return null;
+ let callee = getType(resolvedFunc.expr.type);
+ if (!callee.generic_ret) return null;
+ resolvedGenericRet = resolveValue({ expr: callee.generic_ret });
+ }
+
+ if ("type" in resolvedGenericRet.expr) {
+ parentType = getType(resolvedGenericRet.expr.type);
+ }
+ }
+ }
+ }
+ }
+
+ if (parentType.pubDecls) {
+ for (let i = 0; i < parentType.pubDecls.length; i += 1) {
+ let declIndex = parentType.pubDecls[i];
+ let childDecl = getDecl(declIndex);
+ if (childDecl.name === childName) {
+ childDecl.find_subdecl_idx = declIndex;
+ return childDecl;
+ } else if (childDecl.is_uns) {
+ let declValue = resolveValue(childDecl.value);
+ if (!("type" in declValue.expr)) continue;
+ let uns_container = getType(declValue.expr.type);
+ let uns_res = findSubDecl(uns_container, childName);
+ if (uns_res !== null) return uns_res;
+ }
+ }
+ }
+
+ if (parentType.privDecls) {
+ for (let i = 0; i < parentType.privDecls.length; i += 1) {
+ let declIndex = parentType.privDecls[i];
+ let childDecl = getDecl(declIndex);
+ if (childDecl.name === childName) {
+ childDecl.find_subdecl_idx = declIndex;
+ childDecl.is_private = true;
+ return childDecl;
+ } else if (childDecl.is_uns) {
+ let declValue = resolveValue(childDecl.value);
+ if (!("type" in declValue.expr)) continue;
+ let uns_container = getType(declValue.expr.type);
+ let uns_res = findSubDecl(uns_container, childName);
+ uns_res.is_private = true;
+ if (uns_res !== null) return uns_res;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ function computeCanonicalModulePaths() {
+ let list = new Array(zigAnalysis.modules.length);
+ // Now we try to find all the modules from root.
+ let rootMod = zigAnalysis.modules[zigAnalysis.rootMod];
+ // Breadth-first to keep the path shortest possible.
+ let stack = [
+ {
+ path: [],
+ mod: rootMod,
+ },
+ ];
+ while (stack.length !== 0) {
+ let item = stack.shift();
+ for (let key in item.mod.table) {
+ let childModIndex = item.mod.table[key];
+ if (list[childModIndex] != null) continue;
+ let childMod = zigAnalysis.modules[childModIndex];
+ if (childMod == null) continue;
+
+ let newPath = item.path.concat([key]);
+ list[childModIndex] = newPath;
+ stack.push({
+ path: newPath,
+ mod: childMod,
+ });
+ }
+ }
+
+ for (let i = 0; i < zigAnalysis.modules.length; i += 1) {
+ const p = zigAnalysis.modules[i];
+ // TODO
+ // declSearchIndex.add(p.name, {moduleId: i});
+ }
+ return list;
+ }
+
+ function computeCanonDeclPaths() {
+ let list = new Array(zigAnalysis.decls.length);
+ canonTypeDecls = new Array(zigAnalysis.types.length);
+
+ for (let modI = 0; modI < zigAnalysis.modules.length; modI += 1) {
+ let mod = zigAnalysis.modules[modI];
+ let modNames = canonModPaths[modI];
+ if (modNames === undefined) continue;
+
+ let stack = [
+ {
+ declNames: [],
+ declIndexes: [],
+ type: getType(mod.main),
+ },
+ ];
+ while (stack.length !== 0) {
+ let item = stack.shift();
+
+ if (isContainerType(item.type)) {
+ let t = item.type;
+
+ let len = t.pubDecls ? t.pubDecls.length : 0;
+ for (let declI = 0; declI < len; declI += 1) {
+ let declIndex = t.pubDecls[declI];
+ if (list[declIndex] != null) continue;
+
+ let decl = getDecl(declIndex);
+
+ if (decl.is_uns) {
+ let unsDeclList = [decl];
+ while (unsDeclList.length != 0) {
+ let unsDecl = unsDeclList.pop();
+ let unsDeclVal = resolveValue(unsDecl.value);
+ if (!("type" in unsDeclVal.expr)) continue;
+ let unsType = getType(unsDeclVal.expr.type);
+ if (!isContainerType(unsType)) continue;
+ let unsPubDeclLen = unsType.pubDecls ? unsType.pubDecls.length : 0;
+ for (let unsDeclI = 0; unsDeclI < unsPubDeclLen; unsDeclI += 1) {
+ let childDeclIndex = unsType.pubDecls[unsDeclI];
+ let childDecl = getDecl(childDeclIndex);
+
+ if (childDecl.is_uns) {
+ unsDeclList.push(childDecl);
+ } else {
+ addDeclToSearchResults(childDecl, childDeclIndex, modNames, item, list, stack);
+ }
+ }
+ }
+ } else {
+ addDeclToSearchResults(decl, declIndex, modNames, item, list, stack);
+ }
+ }
+ }
+ }
+ }
+ window.cdp = list;
+ return list;
+ }
+
+ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
+ let {value: declVal, seenDecls} = resolveValue(decl.value, true);
+ let declNames = item.declNames.concat([decl.name]);
+ let declIndexes = item.declIndexes.concat([declIndex]);
+
+ if (list[declIndex] != null) return;
+ list[declIndex] = {
+ modNames: modNames,
+ declNames: declNames,
+ declIndexes: declIndexes,
+ };
+
+ for (let sd of seenDecls) {
+ if (list[sd] != null) continue;
+ list[sd] = {
+ modNames: modNames,
+ declNames: declNames,
+ declIndexes: declIndexes,
+ };
+ }
+
+ // add to search index
+ {
+ declSearchIndex.add(decl.name, { declIndex });
+ }
+
+
+ if ("type" in declVal.expr) {
+ let value = getType(declVal.expr.type);
+ if (declCanRepresentTypeKind(value.kind)) {
+ canonTypeDecls[declVal.type] = declIndex;
+ }
+
+ if (isContainerType(value)) {
+ stack.push({
+ declNames: declNames,
+ declIndexes: declIndexes,
+ type: value,
+ });
+ }
+
+ // Generic function
+ if (typeIsGenericFn(declVal.expr.type)) {
+ let ret = resolveGenericRet(value);
+ if (ret != null && "type" in ret.expr) {
+ let generic_type = getType(ret.expr.type);
+ if (isContainerType(generic_type)) {
+ stack.push({
+ declNames: declNames,
+ declIndexes: declIndexes,
+ type: generic_type,
+ });
+ }
+ }
+ }
+ }
+ }
+
+ function declLinkOrSrcLink(index) {
+
+ let match = getCanonDeclPath(index);
+ if (match) return navLink(match.modNames, match.declNames);
+
+ // could not find a precomputed decl path
+ const decl = getDecl(index);
+
+ // try to find a public decl by scanning declRefs and declPaths
+ let value = decl.value;
+ let i = 0;
+ while (true) {
+ i += 1;
+ if (i >= 10000) {
+ throw "getCanonDeclPath quota exceeded"
+ }
+
+ if ("refPath" in value.expr) {
+ value = { expr: value.expr.refPath[value.expr.refPath.length - 1] };
+ continue;
+ }
+
+ if ("declRef" in value.expr) {
+ let cp = canonDeclPaths[value.expr.declRef];
+ if (cp) return navLink(cp.modNames, cp.declNames);
+
+ value = getDecl(value.expr.declRef).value;
+ continue;
+ }
+
+ if ("as" in value.expr) {
+ value = {
+ typeRef: zigAnalysis.exprs[value.expr.as.typeRefArg],
+ expr: zigAnalysis.exprs[value.expr.as.exprArg],
+ };
+ continue;
+ }
+
+ // if we got here it means that we failed
+ // produce a link to source code instead
+ return sourceFileLink(decl);
+
+ }
+
+ }
+
+ function getCanonDeclPath(index) {
+ if (canonDeclPaths == null) {
+ canonDeclPaths = computeCanonDeclPaths();
+ }
+
+ return canonDeclPaths[index];
+
+
+ }
+
+ function getCanonTypeDecl(index) {
+ getCanonDeclPath(0);
+ //let ct = (canonTypeDecls);
+ return canonTypeDecls[index];
+ }
+
+ function escapeHtml(text) {
+ return text.replace(/[&"<>]/g, function(m) {
+ return escapeHtmlReplacements[m];
+ });
+ }
+
+ function shortDesc(docs) {
+ const trimmed_docs = docs.trim();
+ let index = trimmed_docs.indexOf("\n\n");
+ let cut = false;
+
+ if (index < 0 || index > 130) {
+ if (trimmed_docs.length > 130) {
+ index = 130;
+ cut = true;
+ } else {
+ index = trimmed_docs.length;
+ }
+ }
+
+ let slice = trimmed_docs.slice(0, index);
+ if (cut) slice += "...";
+ return slice;
+ }
+
+ function shortDescMarkdown(docs) {
+ return markdown(shortDesc(docs));
+ }
+
+ function parseGuides() {
+ for (let j = 0; j < zigAnalysis.guideSections.length; j += 1) {
+ const section = zigAnalysis.guideSections[j];
+ for (let i = 0; i < section.guides.length; i += 1) {
+ let reader = new commonmark.Parser({ smart: true });
+ const guide = section.guides[i];
+
+ // Find the first text thing to use as a sidebar title
+ guide.title = null;
+ guide.toc = "";
+
+ // Discover Title & TOC for this guide
+ {
+ let reader = new commonmark.Parser({smart: true});
+ let ast = reader.parse(guide.body);
+ let walker = ast.walker();
+ let heading_idx = 0;
+ let event, node, doc, last, last_ul;
+ while ((event = walker.next())) {
+ node = event.node;
+ if (event.entering) {
+ if (node.type === 'document') {
+ doc = node;
+ continue;
+ }
+
+
+ if (node.next) {
+ walker.resumeAt(node.next, true);
+ } else {
+ walker.resumeAt(node, false);
+ }
+ node.unlink();
+
+ if (node.type === 'heading') {
+ if (node.level == 1) {
+ if (guide.title == null) {
+ let doc_node = new commonmark.Node("document", node.sourcepos);
+ while (node.firstChild) {
+ doc_node.appendChild(node.firstChild);
+ }
+ let writer = new commonmark.HtmlRenderer();
+ let result = writer.render(doc_node);
+ guide.title = result;
+ }
+
+ continue; // don't index H1
+ }
+
+ // turn heading node into list item & add link node to it
+ {
+ node._type = "link";
+ node.destination = NAV_MODES.GUIDES + guide.name + ":" + heading_idx;
+ heading_idx += 1;
+ let listItem = new commonmark.Node("item", node.sourcepos);
+ // TODO: strip links from inside node
+ listItem.appendChild(node);
+ listItem.level = node.level;
+ node = listItem;
+ }
+
+ if (last_ul) {
+ // are we inside or outside of it?
+
+ let target_ul = last_ul;
+ while(target_ul.level > node.level) {
+ target_ul = target_ul.parent;
+ }
+ while(target_ul.level < node.level) {
+ let ul_node = new commonmark.Node("list", node.sourcepos);
+ ul_node.level = target_ul.level + 1;
+ ul_node.listType = "bullet";
+ ul_node.listStart = null;
+ target_ul.appendChild(ul_node);
+ target_ul = ul_node;
+ }
+
+ target_ul.appendChild(node);
+ last_ul = target_ul;
+ } else {
+ let ul_node = new commonmark.Node("list", node.sourcepos);
+ ul_node.level = 2;
+ ul_node.listType = "bullet";
+ ul_node.listStart = null;
+ doc.prependChild(ul_node);
+
+ while (ul_node.level < node.level) {
+ let current_ul_node = new commonmark.Node("list", node.sourcepos);
+ current_ul_node.level = ul_node.level + 1;
+ current_ul_node.listType = "bullet";
+ current_ul_node.listStart = null;
+ ul_node.appendChild(current_ul_node);
+ ul_node = current_ul_node;
+ }
+
+ last_ul = ul_node;
+
+ ul_node.appendChild(node);
+ }
+ }
+ }
+ }
+
+ let writer = new commonmark.HtmlRenderer();
+ let result = writer.render(ast);
+ guide.toc = result;
+ }
+
+ // Index this guide
+ {
+ // let walker = guide.ast.walker();
+ // let event, node;
+ // while ((event = walker.next())) {
+ // node = event.node;
+ // if (event.entering == true && node.type === 'text') {
+ // indexTextForGuide(j, i, node);
+ // }
+ // }
+ }
+ }
+ }
+ }
+
+ function indexTextForGuide(section_idx, guide_idx, node) {
+ const terms = node.literal.split(" ");
+ for (let i = 0; i < terms.length; i += 1) {
+ const t = terms[i];
+ if (!guidesSearchIndex[t]) guidesSearchIndex[t] = new Set();
+ node.guide = { section_idx, guide_idx };
+ guidesSearchIndex[t].add(node);
+ }
+ }
+
+
+ function markdown(input, contextType) {
+ const parsed = new commonmark.Parser({ smart: true }).parse(input);
+
+ // Look for decl references in inline code (`ref`)
+ const walker = parsed.walker();
+ let event;
+ while ((event = walker.next())) {
+ const node = event.node;
+ if (node.type === "code") {
+ const declHash = detectDeclPath(node.literal, contextType);
+ if (declHash) {
+ const link = new commonmark.Node("link");
+ link.destination = declHash;
+ node.insertBefore(link);
+ link.appendChild(node);
+ }
+ }
+ }
+
+ return new commonmark.HtmlRenderer({ safe: true }).render(parsed);
+
+ }
+
+
+
+ // function detectDeclPath(text, context) {
+ // let result = "";
+ // let separator = ":";
+ // const components = text.split(".");
+ // let curDeclOrType = undefined;
+
+ // let curContext = context;
+ // let limit = 10000;
+ // while (curContext) {
+ // limit -= 1;
+
+ // if (limit == 0) {
+ // throw "too many iterations";
+ // }
+
+ // curDeclOrType = findSubDecl(curContext, components[0]);
+
+ // if (!curDeclOrType) {
+ // if (curContext.parent_container == null) break;
+ // curContext = getType(curContext.parent_container);
+ // continue;
+ // }
+
+ // if (curContext == context) {
+ // separator = '.';
+ // result = location.hash + separator + components[0];
+ // } else {
+ // // We had to go up, which means we need a new path!
+ // const canonPath = getCanonDeclPath(curDeclOrType.find_subdecl_idx);
+ // if (!canonPath) return;
+
+ // let lastModName = canonPath.modNames[canonPath.modNames.length - 1];
+ // let fullPath = lastModName + ":" + canonPath.declNames.join(".");
+
+ // separator = '.';
+ // result = "#A;" + fullPath;
+ // }
+
+ // break;
+ // }
+
+ // if (!curDeclOrType) {
+ // for (let i = 0; i < zigAnalysis.modules.length; i += 1) {
+ // const p = zigAnalysis.modules[i];
+ // if (p.name == components[0]) {
+ // curDeclOrType = getType(p.main);
+ // result += "#A;" + components[0];
+ // break;
+ // }
+ // }
+ // }
+
+ // if (!curDeclOrType) return null;
+
+ // for (let i = 1; i < components.length; i += 1) {
+ // curDeclOrType = findSubDecl(curDeclOrType, components[i]);
+ // if (!curDeclOrType) return null;
+ // result += separator + components[i];
+ // separator = '.';
+ // }
+
+ // return result;
+
+ // }
+
+ function activateSelectedResult() {
+ if (domSectSearchResults.classList.contains("hidden")) {
+ return;
+ }
+
+ const searchResults = domListSearchResults.getElementsByTagName("li");
+ let liDom = searchResults[curSearchIndex];
+ if (liDom == null && searchResults.length !== 0) {
+ liDom = searchResults[0];
+ }
+ if (liDom != null) {
+ let aDom = liDom.children[0];
+ location.href = aDom.getAttribute("href");
+ curSearchIndex = -1;
+ }
+ domSearch.blur();
+ }
+
+ // hide the modal if it's visible or return to the previous result page and unfocus the search
+ function onEscape(ev) {
+ if (isModalVisible(domHelpModal)) {
+ hideModal(domHelpModal);
+ ev.preventDefault();
+ ev.stopPropagation();
+ } else if (isModalVisible(domPrefsModal)) {
+ hideModal(domPrefsModal);
+ ev.preventDefault();
+ ev.stopPropagation();
+ } else {
+ domSearch.value = "";
+ domSearch.blur();
+ domSearchPlaceholder.classList.remove("hidden");
+ curSearchIndex = -1;
+ ev.preventDefault();
+ ev.stopPropagation();
+ startSearch();
+ }
+ }
+
+
+ function onSearchKeyDown(ev) {
+ switch (getKeyString(ev)) {
+ case "Enter":
+ // detect if this search changes anything
+ let terms1 = getSearchTerms();
+ startSearch();
+ updateCurNav();
+ let terms2 = getSearchTerms();
+ // we might have to wait for onHashChange to trigger
+ imFeelingLucky = terms1.join(" ") !== terms2.join(" ");
+ if (!imFeelingLucky) activateSelectedResult();
+
+ ev.preventDefault();
+ ev.stopPropagation();
+ return;
+ case "Esc":
+ onEscape(ev);
+ return
+ case "Up":
+ moveSearchCursor(-1);
+ ev.preventDefault();
+ ev.stopPropagation();
+ return;
+ case "Down":
+ // TODO: make the page scroll down if the search cursor is out of the screen
+ moveSearchCursor(1);
+ ev.preventDefault();
+ ev.stopPropagation();
+ return;
+ default:
+ // Search is triggered via an `input` event handler, not on arbitrary `keydown` events.
+ ev.stopPropagation();
+ return;
+ }
+ }
+
+ let domDotsToggleTimeout = null;
+ function onSearchInput(ev) {
+ curSearchIndex = -1;
+
+ let replaced = domSearch.value.replaceAll(".", " ")
+
+ // Ping red the help text if the user typed a dot.
+ if (replaced != domSearch.value) {
+ domSearchHelpSummary.classList.remove("normal");
+ if (domDotsToggleTimeout != null) {
+ clearTimeout(domDotsToggleTimeout);
+ domDotsToggleTimeout = null;
+ }
+ domDotsToggleTimeout = setTimeout(function () {
+ domSearchHelpSummary.classList.add("normal");
+ }, 1000);
+ }
+
+ replaced = replaced.replace(/ +/g, ' ');
+ if (replaced != domSearch.value) {
+ domSearch.value = replaced;
+ }
+
+ startAsyncSearch();
+ }
+
+ function moveSearchCursor(dir) {
+ const searchResults = domListSearchResults.getElementsByTagName("li");
+ if (
+ curSearchIndex < 0 ||
+ curSearchIndex >= searchResults.length
+ ) {
+ if (dir > 0) {
+ curSearchIndex = -1 + dir;
+ } else if (dir < 0) {
+ curSearchIndex = searchResults.length + dir;
+ }
+ } else {
+ curSearchIndex += dir;
+ }
+ if (curSearchIndex < 0) {
+ curSearchIndex = 0;
+ }
+ if (curSearchIndex >= searchResults.length) {
+ curSearchIndex = searchResults.length - 1;
+ }
+ renderSearchCursor();
+ }
+
+ function getKeyString(ev) {
+ let name;
+ let ignoreShift = false;
+ switch (ev.which) {
+ case 13:
+ name = "Enter";
+ break;
+ case 27:
+ name = "Esc";
+ break;
+ case 38:
+ name = "Up";
+ break;
+ case 40:
+ name = "Down";
+ break;
+ default:
+ ignoreShift = true;
+ name =
+ ev.key != null
+ ? ev.key
+ : String.fromCharCode(ev.charCode || ev.keyCode);
+ }
+ if (!ignoreShift && ev.shiftKey) name = "Shift+" + name;
+ if (ev.altKey) name = "Alt+" + name;
+ if (ev.ctrlKey) name = "Ctrl+" + name;
+ return name;
+ }
+
+ function onWindowKeyDown(ev) {
+ switch (getKeyString(ev)) {
+ case "Esc":
+ onEscape(ev);
+ break;
+ case "/":
+ if (!getPrefSlashSearch()) break;
+ // fallthrough
+ case "s":
+ if (!isModalVisible(domHelpModal) && !isModalVisible(domPrefsModal)) {
+ if (ev.target == domSearch) break;
+
+ domSearch.focus();
+ domSearch.select();
+ domDocs.scrollTo(0, 0);
+ ev.preventDefault();
+ ev.stopPropagation();
+ startAsyncSearch();
+ }
+ break;
+ case "?":
+ if (!canToggleModal) break;
+
+ if (isModalVisible(domPrefsModal)) {
+ hideModal(domPrefsModal);
+ }
+
+ // toggle the help modal
+ if (isModalVisible(domHelpModal)) {
+ hideModal(domHelpModal);
+ } else {
+ showModal(domHelpModal);
+ }
+ ev.preventDefault();
+ ev.stopPropagation();
+ break;
+ case "p":
+ if (!canToggleModal) break;
+
+ if (isModalVisible(domHelpModal)) {
+ hideModal(domHelpModal);
+ }
+
+ // toggle the preferences modal
+ if (isModalVisible(domPrefsModal)) {
+ hideModal(domPrefsModal);
+ } else {
+ showModal(domPrefsModal);
+ }
+ ev.preventDefault();
+ ev.stopPropagation();
+ }
+ }
+
+ function isModalVisible(modal) {
+ return !modal.classList.contains("hidden");
+ }
+
+ function showModal(modal) {
+ modal.classList.remove("hidden");
+ modal.style.left =
+ window.innerWidth / 2 - modal.clientWidth / 2 + "px";
+ modal.style.top =
+ window.innerHeight / 2 - modal.clientHeight / 2 + "px";
+ const firstInput = modal.querySelector("input");
+ if (firstInput) {
+ firstInput.focus();
+ } else {
+ modal.focus();
+ }
+ domSearch.blur();
+ domBanner.inert = true;
+ domMain.inert = true;
+ }
+
+ function hideModal(modal) {
+ modal.classList.add("hidden");
+ domBanner.inert = false;
+ domMain.inert = false;
+ modal.blur();
+ }
+
+ function clearAsyncSearch() {
+ if (searchTimer != null) {
+ clearTimeout(searchTimer);
+ searchTimer = null;
+ }
+ }
+
+ function startAsyncSearch() {
+ clearAsyncSearch();
+ searchTimer = setTimeout(startSearch, 100);
+ }
+ function startSearch() {
+ clearAsyncSearch();
+ let oldHash = location.hash;
+ let parts = oldHash.split("?");
+ let newPart2 = domSearch.value === "" ? "" : "?" + domSearch.value;
+ location.replace(parts.length === 1 ? oldHash + newPart2 : parts[0] + newPart2);
+ }
+ function getSearchTerms() {
+ let list = curNavSearch.trim().split(/[ \r\n\t]+/);
+ return list;
+ }
+
+ function renderSearchGuides() {
+ const searchTrimmed = false;
+ let ignoreCase = curNavSearch.toLowerCase() === curNavSearch;
+
+ let terms = getSearchTerms();
+ let matchedItems = new Set();
+
+ for (let i = 0; i < terms.length; i += 1) {
+ const nodes = guidesSearchIndex[terms[i]];
+ if (nodes) {
+ for (const n of nodes) {
+ matchedItems.add(n);
+ }
+ }
+ }
+
+
+
+ if (matchedItems.size !== 0) {
+ // Build up the list of search results
+ let matchedItemsHTML = "";
+
+ for (const node of matchedItems) {
+ const text = node.literal;
+ const href = "";
+
+ matchedItemsHTML += "
" + text + "";
+ }
+
+ // Replace the search results using our newly constructed HTML string
+ domListSearchResults.innerHTML = matchedItemsHTML;
+ if (searchTrimmed) {
+ domSectSearchAllResultsLink.classList.remove("hidden");
+ }
+ renderSearchCursor();
+
+ domSectSearchResults.classList.remove("hidden");
+ } else {
+ domSectSearchNoResults.classList.remove("hidden");
+ }
+ }
+
+ function renderSearchAPI() {
+ domSectSearchResults.prepend(
+ domSearchHelp.parentElement.removeChild(domSearchHelp)
+ );
+ if (canonDeclPaths == null) {
+ canonDeclPaths = computeCanonDeclPaths();
+ }
+ let declSet = new Set();
+ let otherDeclSet = new Set(); // for low quality results
+ let declScores = {};
+
+ let ignoreCase = curNavSearch.toLowerCase() === curNavSearch;
+ let term_list = getSearchTerms();
+ for (let i = 0; i < term_list.length; i += 1) {
+ let term = term_list[i];
+ let result = declSearchIndex.search(term.toLowerCase());
+ if (result == null) {
+ domSectSearchNoResults.prepend(
+ domSearchHelp.parentElement.removeChild(domSearchHelp)
+ );
+ domSectSearchNoResults.classList.remove("hidden");
+
+ domSectSearchResults.classList.add("hidden");
+ return;
+ }
+
+ let termSet = new Set();
+ let termOtherSet = new Set();
+
+ for (let list of [result.full, result.partial]) {
+ for (let r of list) {
+ const d = r.declIndex;
+ const decl = getDecl(d);
+ const canonPath = getCanonDeclPath(d);
+
+ // collect unconditionally for the first term
+ if (i == 0) {
+ declSet.add(d);
+ } else {
+ // path intersection for subsequent terms
+ let found = false;
+ for (let p of canonPath.declIndexes) {
+ if (declSet.has(p)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ otherDeclSet.add(d);
+ } else {
+ termSet.add(d);
+ }
+ }
+
+ if (declScores[d] == undefined) declScores[d] = 0;
+
+ // scores (lower is better)
+ let decl_name = decl.name;
+ if (ignoreCase) decl_name = decl_name.toLowerCase();
+
+ // shallow path are preferable
+ const path_depth = canonPath.declNames.length * 50;
+ // matching the start of a decl name is good
+ const match_from_start = decl_name.startsWith(term) ? -term.length * (2 - ignoreCase) : (decl_name.length - term.length) + 1;
+ // being a perfect match is good
+ const is_full_match = (decl_name === term) ? -decl_name.length * (1 - ignoreCase) : Math.abs(decl_name.length - term.length);
+ // matching the end of a decl name is good
+ const matches_the_end = decl_name.endsWith(term) ? -term.length * (1 - ignoreCase) : (decl_name.length - term.length) + 1;
+ // explicitly penalizing scream case decls
+ const decl_is_scream_case = decl.name.toUpperCase() != decl.name ? 0 : decl.name.length;
+
+ const score = path_depth
+ + match_from_start
+ + is_full_match
+ + matches_the_end
+ + decl_is_scream_case;
+
+ declScores[d] += score;
+ }
+ }
+ if (i != 0) {
+ for (let d of declSet) {
+ if (termSet.has(d)) continue;
+ let found = false;
+ for (let p of getCanonDeclPath(d).declIndexes) {
+ if (termSet.has(p) || otherDeclSet.has(p)) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ declScores[d] = declScores[d] / term_list.length;
+ }
+
+ termOtherSet.add(d);
+ }
+ declSet = termSet;
+ for (let d of termOtherSet) {
+ otherDeclSet.add(d);
+ }
+
+ }
+ }
+
+ let matchedItems = {
+ high_quality: [],
+ low_quality: [],
+ };
+ for (let idx of declSet) {
+ matchedItems.high_quality.push({ points: declScores[idx], declIndex: idx })
+ }
+ for (let idx of otherDeclSet) {
+ matchedItems.low_quality.push({ points: declScores[idx], declIndex: idx })
+ }
+
+ matchedItems.high_quality.sort(function(a, b) {
+ let cmp = operatorCompare(a.points, b.points);
+ return cmp;
+ });
+ matchedItems.low_quality.sort(function(a, b) {
+ let cmp = operatorCompare(a.points, b.points);
+ return cmp;
+ });
+
+ // Build up the list of search results
+ let matchedItemsHTML = "";
+
+ for (let list of [matchedItems.high_quality, matchedItems.low_quality]) {
+ if (list == matchedItems.low_quality && list.length > 0) {
+ matchedItemsHTML += "
"
+ }
+ for (let result of list) {
+ const points = result.points;
+ const match = result.declIndex;
+
+ let canonPath = getCanonDeclPath(match);
+ if (canonPath == null) continue;
+
+ let lastModName = canonPath.modNames[canonPath.modNames.length - 1];
+ let text = lastModName + "." + canonPath.declNames.join(".");
+
+
+ const href = navLink(canonPath.modNames, canonPath.declNames);
+
+ matchedItemsHTML += "
" + text + "";
+ }
+ }
+
+ // Replace the search results using our newly constructed HTML string
+ domListSearchResults.innerHTML = matchedItemsHTML;
+ renderSearchCursor();
+
+ domSectSearchResults.classList.remove("hidden");
+ }
+
+
+
+ function renderSearchCursor() {
+ const searchResults = domListSearchResults.getElementsByTagName("li");
+ for (let i = 0; i < searchResults.length; i += 1) {
+ let liDom = searchResults[i];
+ if (curSearchIndex === i) {
+ liDom.classList.add("selected");
+ } else {
+ liDom.classList.remove("selected");
+ }
+ }
+ }
+
+ function scrollGuidesTop(ev) {
+ document.getElementById("activeGuide").children[0].scrollIntoView({
+ behavior: "smooth",
+ });
+ ev.preventDefault();
+ ev.stopPropagation();
+ }
+ document.scrollGuidesTop = scrollGuidesTop;
+
+ function scrollToHeading(id, alreadyThere) {
+ // Don't scroll if the current location has a scrolling history.
+ if (scrollHistory[location.hash]) return;
+
+ const c = document.getElementById(id);
+ if (c && alreadyThere) {
+ requestAnimationFrame(() => c.scrollIntoView({behavior: "smooth"}));
+ } else {
+ requestAnimationFrame(() => c.scrollIntoView());
+ }
+ return;
+ }
+ // function indexNodesToCalls() {
+ // let map = {};
+ // for (let i = 0; i < zigAnalysis.calls.length; i += 1) {
+ // let call = zigAnalysis.calls[i];
+ // let fn = zigAnalysis.fns[call.fn];
+ // if (map[fn.src] == null) {
+ // map[fn.src] = [i];
+ // } else {
+ // map[fn.src].push(i);
+ // }
+ // }
+ // return map;
+ // }
+
+ function byNameProperty(a, b) {
+ return operatorCompare(a.name, b.name);
+ }
+
+
+ function getDecl(idx) {
+ const decl = zigAnalysis.decls[idx];
+ return {
+ name: decl[0],
+ kind: decl[1],
+ src: decl[2],
+ value: decl[3],
+ decltest: decl[4],
+ is_uns: decl[5],
+ parent_container: decl[6],
+ };
+ }
+
+ function getAstNode(idx) {
+ const ast = zigAnalysis.astNodes[idx];
+ return {
+ file: ast[0],
+ line: ast[1],
+ col: ast[2],
+ name: ast[3],
+ code: ast[4],
+ docs: ast[5],
+ fields: ast[6],
+ comptime: ast[7],
+ };
+ }
+
+ function getFile(idx) {
+ const file = zigAnalysis.files[idx];
+ return {
+ name: file[0],
+ modIndex: file[1],
+ };
+ }
+
+ function getType(idx) {
+ const ty = zigAnalysis.types[idx];
+ switch (ty[0]) {
+ default:
+ throw "unhandled type kind!";
+ case typeKinds.Unanalyzed:
+ throw "unanalyzed type!";
+ case typeKinds.Type:
+ case typeKinds.Void:
+ case typeKinds.Bool:
+ case typeKinds.NoReturn:
+ case typeKinds.Int:
+ case typeKinds.Float:
+ return { kind: ty[0], name: ty[1] };
+ case typeKinds.Pointer:
+ return {
+ kind: ty[0],
+ size: ty[1],
+ child: ty[2],
+ sentinel: ty[3],
+ align: ty[4],
+ address_space: ty[5],
+ bit_start: ty[6],
+ host_size: ty[7],
+ is_ref: ty[8],
+ is_allowzero: ty[9],
+ is_mutable: ty[10],
+ is_volatile: ty[11],
+ has_sentinel: ty[12],
+ has_align: ty[13],
+ has_addrspace: ty[14],
+ has_bit_range: ty[15],
+ };
+ case typeKinds.Array:
+ return {
+ kind: ty[0],
+ len: ty[1],
+ child: ty[2],
+ sentinel: ty[3],
+ };
+ case typeKinds.Struct:
+ return {
+ kind: ty[0],
+ name: ty[1],
+ src: ty[2],
+ privDecls: ty[3],
+ pubDecls: ty[4],
+ field_types: ty[5],
+ field_defaults: ty[6],
+ backing_int: ty[7],
+ is_tuple: ty[8],
+ line_number: ty[9],
+ parent_container: ty[10],
+ layout: ty[11],
+ };
+ case typeKinds.ComptimeExpr:
+ case typeKinds.ComptimeFloat:
+ case typeKinds.ComptimeInt:
+ case typeKinds.Undefined:
+ case typeKinds.Null:
+ return { kind: ty[0], name: ty[1] };
+ case typeKinds.Optional:
+ return {
+ kind: ty[0],
+ name: ty[1],
+ child: ty[2],
+ };
+ case typeKinds.ErrorUnion:
+ return {
+ kind: ty[0],
+ lhs: ty[1],
+ rhs: ty[2],
+ };
+ case typeKinds.InferredErrorUnion:
+ return {
+ kind: ty[0],
+ payload: ty[1],
+ };
+ case typeKinds.ErrorSet:
+ return {
+ kind: ty[0],
+ name: ty[1],
+ fields: ty[2],
+ };
+ case typeKinds.Enum:
+ return {
+ kind: ty[0],
+ name: ty[1],
+ src: ty[2],
+ privDecls: ty[3],
+ pubDecls: ty[4],
+ tag: ty[5],
+ values: ty[6],
+ nonexhaustive: ty[7],
+ parent_container: ty[8],
+ };
+ case typeKinds.Union:
+ return {
+ kind: ty[0],
+ name: ty[1],
+ src: ty[2],
+ privDecls: ty[3],
+ pubDecls: ty[4],
+ field_types: ty[5],
+ tag: ty[6],
+ auto_tag: ty[7],
+ parent_container: ty[8],
+ layout: ty[9],
+ };
+ case typeKinds.Fn:
+ return {
+ kind: ty[0],
+ name: ty[1],
+ src: ty[2],
+ ret: ty[3],
+ generic_ret: ty[4],
+ params: ty[5],
+ lib_name: ty[6],
+ is_var_args: ty[7],
+ is_inferred_error: ty[8],
+ has_lib_name: ty[9],
+ has_cc: ty[10],
+ cc: ty[11],
+ align: ty[12],
+ has_align: ty[13],
+ is_test: ty[14],
+ is_extern: ty[15],
+ };
+ case typeKinds.Opaque:
+ return {
+ kind: ty[0],
+ name: ty[1],
+ src: ty[2],
+ privDecls: ty[3],
+ pubDecls: ty[4],
+ parent_container: ty[5],
+ };
+ case typeKinds.Frame:
+ case typeKinds.AnyFrame:
+ case typeKinds.Vector:
+ case typeKinds.EnumLiteral:
+ return { kind: ty[0], name: ty[1] };
+ }
+ }
+
+ function getLocalStorage() {
+ if ("localStorage" in window) {
+ try {
+ return window.localStorage;
+ } catch (ignored) {
+ // localStorage may be disabled (SecurityError)
+ }
+ }
+ // If localStorage isn't available, persist preferences only for the current session
+ const sessionPrefs = {};
+ return {
+ getItem(key) {
+ return key in sessionPrefs ? sessionPrefs[key] : null;
+ },
+ setItem(key, value) {
+ sessionPrefs[key] = String(value);
+ },
+ };
+ }
+
+ function loadPrefs() {
+ const storedPrefSlashSearch = prefs.getItem("slashSearch");
+ if (storedPrefSlashSearch === null) {
+ // Slash search defaults to enabled for all browsers except Firefox
+ setPrefSlashSearch(navigator.userAgent.indexOf("Firefox") === -1);
+ } else {
+ setPrefSlashSearch(storedPrefSlashSearch === "true");
+ }
+ }
+
+ function getPrefSlashSearch() {
+ return prefs.getItem("slashSearch") === "true";
+ }
+
+ function setPrefSlashSearch(enabled) {
+ prefs.setItem("slashSearch", String(enabled));
+ domPrefSlashSearch.checked = enabled;
+ const searchKeys = enabled ? "
/ or
s" : "
s";
+ domSearchKeys.innerHTML = searchKeys;
+ domSearchPlaceholderText.innerHTML = searchKeys + " to search,
? for more options";
+ }
+})();
+
+function toggleExpand(event) {
+ const parent = event.target.parentElement;
+ parent.toggleAttribute("open");
+
+ if (!parent.open && parent.getBoundingClientRect().top < 0) {
+ parent.parentElement.parentElement.scrollIntoView(true);
+ }
+}
+
+function RadixTree() {
+ this.root = null;
+
+ RadixTree.prototype.search = function(query) {
+ return this.root.search(query);
+
+ }
+
+ RadixTree.prototype.add = function(declName, value) {
+ if (this.root == null) {
+ this.root = new Node(declName.toLowerCase(), null, [value]);
+ } else {
+ this.root.add(declName.toLowerCase(), value);
+ }
+
+ const not_scream_case = declName.toUpperCase() != declName;
+ let found_separator = false;
+ for (let i = 1; i < declName.length; i += 1) {
+ if (declName[i] == '_' || declName[i] == '.') {
+ found_separator = true;
+ continue;
+ }
+
+
+ if (found_separator || (declName[i].toLowerCase() !== declName[i])) {
+ if (declName.length > i + 1
+ && declName[i + 1].toLowerCase() != declName[i + 1]) continue;
+ let suffix = declName.slice(i);
+ this.root.add(suffix.toLowerCase(), value);
+ found_separator = false;
+ }
+ }
+ }
+
+ function Node(labels, next, values) {
+ this.labels = labels;
+ this.next = next;
+ this.values = values;
+ }
+
+ Node.prototype.isCompressed = function() {
+ return !Array.isArray(this.next);
+ }
+
+ Node.prototype.search = function(word) {
+ let full_matches = [];
+ let partial_matches = [];
+ let subtree_root = null;
+
+ let cn = this;
+ char_loop: for (let i = 0; i < word.length;) {
+ if (cn.isCompressed()) {
+ for (let j = 0; j < cn.labels.length; j += 1) {
+ let current_idx = i + j;
+
+ if (current_idx == word.length) {
+ partial_matches = cn.values;
+ subtree_root = cn.next;
+ break char_loop;
+ }
+
+ if (word[current_idx] != cn.labels[j]) return null;
+ }
+
+ // the full label matched
+ let new_idx = i + cn.labels.length;
+ if (new_idx == word.length) {
+ full_matches = cn.values;
+ subtree_root = cn.next;
+ break char_loop;
+ }
+
+
+ i = new_idx;
+ cn = cn.next;
+ continue;
+ } else {
+ for (let j = 0; j < cn.labels.length; j += 1) {
+ if (word[i] == cn.labels[j]) {
+ if (i == word.length - 1) {
+ full_matches = cn.values[j];
+ subtree_root = cn.next[j];
+ break char_loop;
+ }
+
+ let next = cn.next[j];
+ if (next == null) return null;
+ cn = next;
+ i += 1;
+ continue char_loop;
+ }
+ }
+
+ // didn't find a match
+ return null;
+ }
+ }
+
+ // Match was found, let's collect all other
+ // partial matches from the subtree
+ let stack = [subtree_root];
+ let node;
+ while (node = stack.pop()) {
+ if (node.isCompressed()) {
+ partial_matches = partial_matches.concat(node.values);
+ if (node.next != null) {
+ stack.push(node.next);
+ }
+ } else {
+ for (let v of node.values) {
+ partial_matches = partial_matches.concat(v);
+ }
+
+ for (let n of node.next) {
+ if (n != null) stack.push(n);
+ }
+ }
+ }
+
+ return { full: full_matches, partial: partial_matches };
+ }
+
+ Node.prototype.add = function(word, value) {
+ let cn = this;
+ char_loop: for (let i = 0; i < word.length;) {
+ if (cn.isCompressed()) {
+ for (let j = 0; j < cn.labels.length; j += 1) {
+ let current_idx = i + j;
+
+ if (current_idx == word.length) {
+ if (j < cn.labels.length - 1) {
+ let node = new Node(cn.labels.slice(j), cn.next, cn.values);
+ cn.labels = cn.labels.slice(0, j);
+ cn.next = node;
+ cn.values = [];
+ }
+ cn.values.push(value);
+ return;
+ }
+
+ if (word[current_idx] == cn.labels[j]) continue;
+
+ // if we're here, a mismatch was found
+ if (j != cn.labels.length - 1) {
+ // create a suffix node
+ const label_suffix = cn.labels.slice(j + 1);
+ let node = new Node(label_suffix, cn.next, [...cn.values]);
+ cn.next = node;
+ cn.values = [];
+ }
+
+ // turn current node into a split node
+ let node = null;
+ let word_values = [];
+ if (current_idx == word.length - 1) {
+ // mismatch happened in the last character of word
+ // meaning that the current node should hold its value
+ word_values.push(value);
+ } else {
+ node = new Node(word.slice(current_idx + 1), null, [value]);
+ }
+
+ cn.labels = cn.labels[j] + word[current_idx];
+ cn.next = [cn.next, node];
+ cn.values = [cn.values, word_values];
+
+ if (j != 0) {
+ // current node must be turned into a prefix node
+ let splitNode = new Node(cn.labels, cn.next, cn.values);
+ cn.labels = word.slice(i, current_idx);
+ cn.next = splitNode;
+ cn.values = [];
+ }
+
+ return;
+ }
+ // label matched fully with word, are there any more chars?
+ const new_idx = i + cn.labels.length;
+ if (new_idx == word.length) {
+ cn.values.push(value);
+ return;
+ } else {
+ if (cn.next == null) {
+ let node = new Node(word.slice(new_idx), null, [value]);
+ cn.next = node;
+ return;
+ } else {
+ cn = cn.next;
+ i = new_idx;
+ continue;
+ }
+ }
+ } else { // node is not compressed
+ let letter = word[i];
+ for (let j = 0; j < cn.labels.length; j += 1) {
+ if (letter == cn.labels[j]) {
+ if (i == word.length - 1) {
+ cn.values[j].push(value);
+ return;
+ }
+ if (cn.next[j] == null) {
+ let node = new Node(word.slice(i + 1), null, [value]);
+ cn.next[j] = node;
+ return;
+ } else {
+ cn = cn.next[j];
+ i += 1;
+ continue char_loop;
+ }
+ }
+ }
+
+ // if we're here we didn't find a match
+ cn.labels += letter;
+ if (i == word.length - 1) {
+ cn.next.push(null);
+ cn.values.push([value]);
+ } else {
+ let node = new Node(word.slice(i + 1), null, [value]);
+ cn.next.push(node);
+ cn.values.push([]);
+ }
+ return;
+ }
+ }
+ }
+}
+
+
+function slugify(str) {
+ return str.toLowerCase().trim().replace(/[^\w\s-]/g, '').replace(/[\s_-]+/g, '-').replace(/^-+|-+$/g, '');
+}
+
diff --git a/docs/src/builtin/builtin.zig.html b/docs/src/builtin/builtin.zig.html
new file mode 100644
index 0000000..0fcd02d
--- /dev/null
+++ b/docs/src/builtin/builtin.zig.html
@@ -0,0 +1,241 @@
+
+
+
+
+
builtin.zig - source view
+
+
+
+
+
+
const std = @import("std");
+
+
+pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable;
+pub const zig_version_string = "0.12.0-dev.2105+60094cc3f";
+pub const zig_backend = std.builtin.CompilerBackend.stage2_llvm;
+
+pub const output_mode = std.builtin.OutputMode.Obj;
+pub const link_mode = std.builtin.LinkMode.Static;
+pub const is_test = false;
+pub const single_threaded = false;
+pub const abi = std.Target.Abi.gnu;
+pub const cpu: std.Target.Cpu = .{
+ .arch = .x86_64,
+ .model = &std.Target.x86.cpu.znver3,
+ .features = std.Target.x86.featureSet(&[_]std.Target.x86.Feature{
+ .@"64bit",
+ .adx,
+ .aes,
+ .allow_light_256_bit,
+ .avx,
+ .avx2,
+ .bmi,
+ .bmi2,
+ .branchfusion,
+ .clflushopt,
+ .clwb,
+ .clzero,
+ .cmov,
+ .crc32,
+ .cx16,
+ .cx8,
+ .f16c,
+ .fast_15bytenop,
+ .fast_bextr,
+ .fast_lzcnt,
+ .fast_movbe,
+ .fast_scalar_fsqrt,
+ .fast_scalar_shift_masks,
+ .fast_variable_perlane_shuffle,
+ .fast_vector_fsqrt,
+ .fma,
+ .fsgsbase,
+ .fsrm,
+ .fxsr,
+ .invpcid,
+ .lzcnt,
+ .macrofusion,
+ .mmx,
+ .movbe,
+ .mwaitx,
+ .nopl,
+ .pclmul,
+ .pku,
+ .popcnt,
+ .prfchw,
+ .rdpid,
+ .rdpru,
+ .rdrnd,
+ .rdseed,
+ .sahf,
+ .sbb_dep_breaking,
+ .sha,
+ .shstk,
+ .slow_shld,
+ .sse,
+ .sse2,
+ .sse3,
+ .sse4_1,
+ .sse4_2,
+ .sse4a,
+ .ssse3,
+ .vaes,
+ .vpclmulqdq,
+ .vzeroupper,
+ .wbnoinvd,
+ .x87,
+ .xsave,
+ .xsavec,
+ .xsaveopt,
+ .xsaves,
+ }),
+};
+pub const os = std.Target.Os{
+ .tag = .linux,
+ .version_range = .{ .linux = .{
+ .range = .{
+ .min = .{
+ .major = 6,
+ .minor = 6,
+ .patch = 3,
+ },
+ .max = .{
+ .major = 6,
+ .minor = 6,
+ .patch = 3,
+ },
+ },
+ .glibc = .{
+ .major = 2,
+ .minor = 36,
+ .patch = 0,
+ },
+ }},
+};
+pub const target: std.Target = .{
+ .cpu = cpu,
+ .os = os,
+ .abi = abi,
+ .ofmt = object_format,
+ .dynamic_linker = std.Target.DynamicLinker.init("/nix/store/whypqfa83z4bsn43n4byvmw80n4mg3r8-glibc-2.37-45/lib/ld-linux-x86-64.so.2"),
+};
+pub const object_format = std.Target.ObjectFormat.elf;
+pub const mode = std.builtin.OptimizeMode.Debug;
+pub const link_libc = false;
+pub const link_libcpp = false;
+pub const have_error_return_tracing = true;
+pub const valgrind_support = true;
+pub const sanitize_thread = false;
+pub const position_independent_code = false;
+pub const position_independent_executable = false;
+pub const strip_debug_info = false;
+pub const code_model = std.builtin.CodeModel.default;
+pub const omit_frame_pointer = false;
+
+
+
\ No newline at end of file
diff --git a/docs/src/zap/endpoint.zig.html b/docs/src/zap/endpoint.zig.html
new file mode 100644
index 0000000..f15f03e
--- /dev/null
+++ b/docs/src/zap/endpoint.zig.html
@@ -0,0 +1,461 @@
+
+
+
+
+
endpoint.zig - source view
+
+
+
+
+
+
const std = @import("std");
+const zap = @import("zap.zig");
+const auth = @import("http_auth.zig");
+
+const Endpoint = @This();
+
+
+
+const Request = zap.Request;
+const ListenerSettings = zap.HttpListenerSettings;
+const HttpListener = zap.HttpListener;
+
+
+pub const RequestFn = *const fn (self: *Endpoint, r: Request) void;
+
+
+pub const Settings = struct {
+
+ path: []const u8,
+
+ get: ?RequestFn = null,
+
+ post: ?RequestFn = null,
+
+ put: ?RequestFn = null,
+
+ delete: ?RequestFn = null,
+
+ patch: ?RequestFn = null,
+
+ options: ?RequestFn = null,
+
+ unauthorized: ?RequestFn = null,
+};
+
+settings: Settings,
+
+
+
+
+pub fn init(s: Settings) Endpoint {
+ return .{
+ .settings = .{
+ .path = s.path,
+ .get = s.get orelse &nop,
+ .post = s.post orelse &nop,
+ .put = s.put orelse &nop,
+ .delete = s.delete orelse &nop,
+ .patch = s.patch orelse &nop,
+ .options = s.options orelse &nop,
+ .unauthorized = s.unauthorized orelse &nop,
+ },
+ };
+}
+
+
+
+fn nop(self: *Endpoint, r: Request) void {
+ _ = self;
+ _ = r;
+}
+
+
+pub fn onRequest(self: *Endpoint, r: zap.Request) void {
+ if (r.method) |m| {
+ if (std.mem.eql(u8, m, "GET"))
+ return self.settings.get.?(self, r);
+ if (std.mem.eql(u8, m, "POST"))
+ return self.settings.post.?(self, r);
+ if (std.mem.eql(u8, m, "PUT"))
+ return self.settings.put.?(self, r);
+ if (std.mem.eql(u8, m, "DELETE"))
+ return self.settings.delete.?(self, r);
+ if (std.mem.eql(u8, m, "PATCH"))
+ return self.settings.patch.?(self, r);
+ if (std.mem.eql(u8, m, "OPTIONS"))
+ return self.settings.options.?(self, r);
+ }
+}
+
+
+
+pub fn Authenticating(comptime Authenticator: type) type {
+ return struct {
+ authenticator: *Authenticator,
+ ep: *Endpoint,
+ auth_endpoint: Endpoint,
+ const Self = @This();
+
+
+
+
+ pub fn init(e: *Endpoint, authenticator: *Authenticator) Self {
+ return .{
+ .authenticator = authenticator,
+ .ep = e,
+ .auth_endpoint = Endpoint.init(.{
+ .path = e.settings.path,
+
+
+
+
+ .get = if (e.settings.get != null) get else null,
+ .post = if (e.settings.post != null) post else null,
+ .put = if (e.settings.put != null) put else null,
+ .delete = if (e.settings.delete != null) delete else null,
+ .patch = if (e.settings.patch != null) patch else null,
+ .options = if (e.settings.options != null) options else null,
+ .unauthorized = e.settings.unauthorized,
+ }),
+ };
+ }
+
+
+
+
+ pub fn endpoint(self: *Self) *Endpoint {
+ return &self.auth_endpoint;
+ }
+
+
+
+ pub fn get(e: *Endpoint, r: zap.Request) void {
+ const authEp: *Self = @fieldParentPtr(Self, "auth_endpoint", e);
+ switch (authEp.authenticator.authenticateRequest(&r)) {
+ .AuthFailed => {
+ if (e.settings.unauthorized) |unauthorized| {
+ unauthorized(authEp.ep, r);
+ return;
+ } else {
+ r.setStatus(.unauthorized);
+ r.sendBody("UNAUTHORIZED") catch return;
+ return;
+ }
+ },
+ .AuthOK => authEp.ep.settings.get.?(authEp.ep, r),
+ .Handled => {},
+ }
+ }
+
+
+
+ pub fn post(e: *Endpoint, r: zap.Request) void {
+ const authEp: *Self = @fieldParentPtr(Self, "auth_endpoint", e);
+ switch (authEp.authenticator.authenticateRequest(&r)) {
+ .AuthFailed => {
+ if (e.settings.unauthorized) |unauthorized| {
+ unauthorized(authEp.ep, r);
+ return;
+ } else {
+ r.setStatus(.unauthorized);
+ r.sendBody("UNAUTHORIZED") catch return;
+ return;
+ }
+ },
+ .AuthOK => authEp.ep.settings.post.?(authEp.ep, r),
+ .Handled => {},
+ }
+ }
+
+
+
+ pub fn put(e: *Endpoint, r: zap.Request) void {
+ const authEp: *Self = @fieldParentPtr(Self, "auth_endpoint", e);
+ switch (authEp.authenticator.authenticateRequest(&r)) {
+ .AuthFailed => {
+ if (e.settings.unauthorized) |unauthorized| {
+ unauthorized(authEp.ep, r);
+ return;
+ } else {
+ r.setStatus(.unauthorized);
+ r.sendBody("UNAUTHORIZED") catch return;
+ return;
+ }
+ },
+ .AuthOK => authEp.ep.settings.put.?(authEp.ep, r),
+ .Handled => {},
+ }
+ }
+
+
+
+ pub fn delete(e: *Endpoint, r: zap.Request) void {
+ const authEp: *Self = @fieldParentPtr(Self, "auth_endpoint", e);
+ switch (authEp.authenticator.authenticateRequest(&r)) {
+ .AuthFailed => {
+ if (e.settings.unauthorized) |unauthorized| {
+ unauthorized(authEp.ep, r);
+ return;
+ } else {
+ r.setStatus(.unauthorized);
+ r.sendBody("UNAUTHORIZED") catch return;
+ return;
+ }
+ },
+ .AuthOK => authEp.ep.settings.delete.?(authEp.ep, r),
+ .Handled => {},
+ }
+ }
+
+
+
+ pub fn patch(e: *Endpoint, r: zap.Request) void {
+ const authEp: *Self = @fieldParentPtr(Self, "auth_endpoint", e);
+ switch (authEp.authenticator.authenticateRequest(&r)) {
+ .AuthFailed => {
+ if (e.settings.unauthorized) |unauthorized| {
+ unauthorized(authEp.ep, r);
+ return;
+ } else {
+ r.setStatus(.unauthorized);
+ r.sendBody("UNAUTHORIZED") catch return;
+ return;
+ }
+ },
+ .AuthOK => authEp.ep.settings.patch.?(authEp.ep, r),
+ .Handled => {},
+ }
+ }
+
+
+
+ pub fn options(e: *Endpoint, r: zap.Request) void {
+ const authEp: *Self = @fieldParentPtr(Self, "auth_endpoint", e);
+ switch (authEp.authenticator.authenticateRequest(&r)) {
+ .AuthFailed => {
+ if (e.settings.unauthorized) |unauthorized| {
+ unauthorized(authEp.ep, r);
+ return;
+ } else {
+ r.setStatus(.unauthorized);
+ r.sendBody("UNAUTHORIZED") catch return;
+ return;
+ }
+ },
+ .AuthOK => authEp.ep.settings.put.?(authEp.ep, r),
+ .Handled => {},
+ }
+ }
+ };
+}
+
+pub const EndpointListenerError = error{
+
+
+
+
+ EndpointPathShadowError,
+};
+
+
+
+
+pub const Listener = struct {
+ listener: HttpListener,
+ allocator: std.mem.Allocator,
+
+ const Self = @This();
+
+
+ var endpoints: std.ArrayList(*Endpoint) = undefined;
+
+
+
+
+ var on_request: ?zap.HttpRequestFn = null;
+
+
+
+
+ pub fn init(a: std.mem.Allocator, l: ListenerSettings) Self {
+ endpoints = std.ArrayList(*Endpoint).init(a);
+
+
+
+ var ls = l;
+
+
+
+
+
+ ls.on_request = Listener.onRequest;
+
+
+
+ on_request = l.on_request;
+ return .{
+ .listener = HttpListener.init(ls),
+ .allocator = a,
+ };
+ }
+
+
+
+
+ pub fn deinit(self: *Self) void {
+ _ = self;
+ endpoints.deinit();
+ }
+
+
+
+ pub fn listen(self: *Self) !void {
+ try self.listener.listen();
+ }
+
+
+
+
+
+ pub fn register(self: *Self, e: *Endpoint) !void {
+ _ = self;
+ for (endpoints.items) |other| {
+ if (std.mem.startsWith(
+ u8,
+ other.settings.path,
+ e.settings.path,
+ ) or std.mem.startsWith(
+ u8,
+ e.settings.path,
+ other.settings.path,
+ )) {
+ return EndpointListenerError.EndpointPathShadowError;
+ }
+ }
+ try endpoints.append(e);
+ }
+
+ fn onRequest(r: Request) void {
+ if (r.path) |p| {
+ for (endpoints.items) |e| {
+ if (std.mem.startsWith(u8, p, e.settings.path)) {
+ e.onRequest(r);
+ return;
+ }
+ }
+ }
+
+
+ if (on_request) |foo| {
+ foo(r);
+ }
+ }
+};
+
+
+
\ No newline at end of file
diff --git a/docs/src/zap/fio.zig.html b/docs/src/zap/fio.zig.html
new file mode 100644
index 0000000..e4e55e4
--- /dev/null
+++ b/docs/src/zap/fio.zig.html
@@ -0,0 +1,740 @@
+
+
+
+
+
fio.zig - source view
+
+
+
+
+
+
pub const FIOBJ = usize;
+pub extern fn is_invalid(o: FIOBJ) c_int;
+pub const fio_url_s = extern struct {
+ scheme: fio_str_info_s,
+ user: fio_str_info_s,
+ password: fio_str_info_s,
+ host: fio_str_info_s,
+ port: fio_str_info_s,
+ path: fio_str_info_s,
+ query: fio_str_info_s,
+ target: fio_str_info_s,
+};
+pub extern fn fio_url_parse(url: [*c]const u8, length: usize) fio_url_s;
+pub const struct_fio_start_args = extern struct {
+ threads: i16,
+ workers: i16,
+};
+pub const fio_start_args = struct_fio_start_args;
+pub extern fn fio_start(args: struct_fio_start_args) void;
+pub extern fn fio_stop() void;
+const struct_unnamed_37 = extern struct {
+ vtbl: ?*anyopaque,
+ flag: usize,
+ out_headers: FIOBJ,
+};
+pub const __time_t = c_long;
+pub const time_t = __time_t;
+pub const __syscall_slong_t = c_long;
+pub const struct_timespec = extern struct {
+ tv_sec: __time_t,
+ tv_nsec: __syscall_slong_t,
+};
+pub const struct_http_settings_s = extern struct {
+ on_request: ?*const fn ([*c]http_s) callconv(.C) void,
+ on_upgrade: ?*const fn ([*c]http_s, [*c]u8, usize) callconv(.C) void,
+ on_response: ?*const fn ([*c]http_s) callconv(.C) void,
+ on_finish: ?*const fn ([*c]struct_http_settings_s) callconv(.C) void,
+ udata: ?*anyopaque,
+ public_folder: [*c]const u8,
+ public_folder_length: usize,
+ max_header_size: usize,
+ max_body_size: usize,
+ max_clients: isize,
+ tls: ?*anyopaque,
+ reserved1: isize,
+ reserved2: isize,
+ reserved3: isize,
+ ws_max_msg_size: usize,
+ timeout: u8,
+ ws_timeout: u8,
+ log: u8,
+ is_client: u8,
+};
+pub const http_settings_s = struct_http_settings_s;
+pub const http_s = extern struct {
+ private_data: struct_unnamed_37,
+ received_at: struct_timespec,
+ method: FIOBJ,
+ status_str: FIOBJ,
+ version: FIOBJ,
+ status: usize,
+ path: FIOBJ,
+ query: FIOBJ,
+ headers: FIOBJ,
+ cookies: FIOBJ,
+ params: FIOBJ,
+ body: FIOBJ,
+ udata: ?*anyopaque,
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+pub const http_cookie_args_s = extern struct {
+ name: [*c]u8,
+ value: [*c]u8,
+ domain: [*c]u8,
+ path: [*c]u8,
+ name_len: isize,
+ value_len: isize,
+ domain_len: isize,
+ path_len: isize,
+
+ max_age: c_int,
+ secure: c_uint,
+ http_only: c_uint,
+};
+
+pub const struct_fio_str_info_s = extern struct {
+ capa: usize,
+ len: usize,
+ data: [*c]u8,
+};
+pub const fio_str_info_s = struct_fio_str_info_s;
+pub extern fn http_send_body(h: [*c]http_s, data: ?*anyopaque, length: usize) c_int;
+pub fn fiobj_each1(arg_o: FIOBJ, arg_start_at: usize, arg_task: ?*const fn (FIOBJ, ?*anyopaque) callconv(.C) c_int, arg_arg: ?*anyopaque) callconv(.C) usize {
+ const o = arg_o;
+ const start_at = arg_start_at;
+ const task = arg_task;
+ const arg = arg_arg;
+ if ((((o != 0) and ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 1))))) == @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 0)))))) and ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 6))))) != @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 6)))))) and (fiobj_type_vtable(o).*.each != null)) return fiobj_type_vtable(o).*.each.?(o, start_at, task, arg);
+ return 0;
+}
+
+pub extern fn fiobj_hash_new() FIOBJ;
+pub extern fn fiobj_hash_set(hash: FIOBJ, key: FIOBJ, obj: FIOBJ) c_int;
+pub extern fn fiobj_hash_get(hash: FIOBJ, key: FIOBJ) FIOBJ;
+pub extern fn fiobj_hash_pop(hash: FIOBJ, key: [*c]FIOBJ) FIOBJ;
+pub extern fn fiobj_hash_count(hash: FIOBJ) usize;
+pub extern fn fiobj_hash_key_in_loop() FIOBJ;
+pub extern fn fiobj_hash_haskey(hash: FIOBJ, key: FIOBJ) c_int;
+pub extern fn fiobj_ary_new() FIOBJ;
+pub extern fn fiobj_ary_new2(capa: usize) FIOBJ;
+pub extern fn fiobj_ary_count(ary: FIOBJ) usize;
+pub extern fn fiobj_ary_capa(ary: FIOBJ) usize;
+pub extern fn fiobj_ary2ptr(ary: FIOBJ) [*c]FIOBJ;
+pub extern fn fiobj_ary_index(ary: FIOBJ, pos: i64) FIOBJ;
+pub extern fn fiobj_ary_set(ary: FIOBJ, obj: FIOBJ, pos: i64) void;
+pub extern fn fiobj_ary_push(ary: FIOBJ, obj: FIOBJ) void;
+pub extern fn fiobj_ary_pop(ary: FIOBJ) FIOBJ;
+pub extern fn fiobj_ary_unshift(ary: FIOBJ, obj: FIOBJ) void;
+pub extern fn fiobj_ary_shift(ary: FIOBJ) FIOBJ;
+pub extern fn fiobj_ary_replace(ary: FIOBJ, obj: FIOBJ, pos: i64) FIOBJ;
+pub extern fn fiobj_ary_find(ary: FIOBJ, data: FIOBJ) i64;
+pub extern fn fiobj_ary_remove(ary: FIOBJ, pos: i64) c_int;
+pub extern fn fiobj_ary_remove2(ary: FIOBJ, data: FIOBJ) c_int;
+pub extern fn fiobj_ary_compact(ary: FIOBJ) void;
+pub extern fn fiobj_float_new(num: f64) FIOBJ;
+pub extern fn fiobj_num_new_bignum(num: isize) FIOBJ;
+
+pub extern fn fiobj_data_newstr() FIOBJ;
+pub extern fn fiobj_data_newstr2(buffer: ?*anyopaque, length: usize, dealloc: ?*const fn (?*anyopaque) callconv(.C) void) FIOBJ;
+pub extern fn fiobj_data_newtmpfile() FIOBJ;
+pub extern fn fiobj_data_newfd(fd: c_int) FIOBJ;
+pub extern fn fiobj_data_slice(parent: FIOBJ, offset: isize, length: usize) FIOBJ;
+pub extern fn fiobj_data_save(io: FIOBJ, filename: [*c]const u8) c_int;
+pub extern fn fiobj_data_read(io: FIOBJ, length: isize) fio_str_info_s;
+pub extern fn fiobj_data_read2ch(io: FIOBJ, token: u8) fio_str_info_s;
+pub extern fn fiobj_data_pos(io: FIOBJ) isize;
+pub extern fn fiobj_data_len(io: FIOBJ) isize;
+pub extern fn fiobj_data_seek(io: FIOBJ, position: isize) void;
+pub extern fn fiobj_data_pread(io: FIOBJ, start_at: isize, length: usize) fio_str_info_s;
+pub extern fn fiobj_data_write(io: FIOBJ, buffer: ?*anyopaque, length: usize) isize;
+pub extern fn fiobj_data_puts(io: FIOBJ, buffer: ?*anyopaque, length: usize) isize;
+pub extern fn fiobj_data_assert_dynamic(io: FIOBJ) void;
+
+
+
+
+
+
+pub extern fn fio_tls_new(
+ server_name: ?[*:0]const u8,
+ public_certificate_file: ?[*:0]const u8,
+ private_key_file: ?[*:0]const u8,
+ private_key_password: ?[*:0]const u8,
+) ?*anyopaque;
+
+
+
+pub extern fn fio_tls_dup(tls: ?*anyopaque) void;
+
+
+pub extern fn fio_tls_destroy(tls: ?*anyopaque) void;
+
+
+
+pub extern fn fio_tls_cert_add(
+ tls: ?*anyopaque,
+ server_name: ?[*:0]const u8,
+ public_certificate_file: ?[*:0]const u8,
+ private_key_file: ?[*:0]const u8,
+ private_key_password: ?[*:0]const u8,
+) c_int;
+
+
+
+
+pub extern fn fio_tls_trust(tls: ?*anyopaque, public_cert_file: ?[*:0]const u8) c_int;
+
+
+
+
+
+pub extern fn fio_tls_accept(uuid: *u32, tls: ?*anyopaque, udata: ?*anyopaque) void;
+
+
+
+
+
+
+pub extern fn fio_tls_connect(uuid: *u32, tls: ?*anyopaque, udata: ?*anyopaque) void;
+
+pub extern fn fiobj_free_wrapped(o: FIOBJ) callconv(.C) void;
+pub fn fiobj_null() callconv(.C) FIOBJ {
+ return @as(FIOBJ, @bitCast(@as(c_long, FIOBJ_T_NULL)));
+}
+pub fn fiobj_true() callconv(.C) FIOBJ {
+ return @as(FIOBJ, @bitCast(@as(c_long, FIOBJ_T_TRUE)));
+}
+pub fn fiobj_false() callconv(.C) FIOBJ {
+ return @as(FIOBJ, @bitCast(@as(c_long, FIOBJ_T_FALSE)));
+}
+pub extern fn fiobj_str_new(str: [*c]const u8, len: usize) FIOBJ;
+pub extern fn fiobj_str_buf(capa: usize) FIOBJ;
+
+pub inline fn FIOBJ_TYPE(obj: anytype) @TypeOf(fiobj_type(obj)) {
+ return fiobj_type(obj);
+}
+pub inline fn FIOBJ_TYPE_IS(obj: anytype, @"type": anytype) @TypeOf(fiobj_type_is(obj, @"type")) {
+ return fiobj_type_is(obj, @"type");
+}
+pub inline fn FIOBJ_IS_NULL(obj: anytype) @TypeOf(!(obj != 0) or (obj == @import("std").zig.c_translation.cast(FIOBJ, FIOBJ_T_NULL))) {
+ return !(obj != 0) or (obj == @import("std").zig.c_translation.cast(FIOBJ, FIOBJ_T_NULL));
+}
+pub const FIOBJ_INVALID = @as(c_int, 0);
+pub const FIOBJECT_NUMBER_FLAG = @as(c_int, 1);
+pub const FIOBJECT_PRIMITIVE_FLAG = @as(c_int, 6);
+pub const FIOBJECT_STRING_FLAG = @as(c_int, 2);
+pub const FIOBJECT_HASH_FLAG = @as(c_int, 4);
+pub const FIOBJECT_TYPE_MASK = ~@import("std").zig.c_translation.cast(usize, @as(c_int, 7));
+pub const FIOBJ_NUMBER_SIGN_MASK = ~@import("std").zig.c_translation.cast(usize, @as(c_int, 0)) >> @as(c_int, 1);
+pub const FIOBJ_NUMBER_SIGN_BIT = ~FIOBJ_NUMBER_SIGN_MASK;
+pub const FIOBJ_NUMBER_SIGN_EXCLUDE_BIT = FIOBJ_NUMBER_SIGN_BIT >> @as(c_int, 1);
+pub inline fn FIOBJ_IS_ALLOCATED(o: anytype) @TypeOf(((o != 0) and ((o & FIOBJECT_NUMBER_FLAG) == @as(c_int, 0))) and ((o & FIOBJECT_PRIMITIVE_FLAG) != FIOBJECT_PRIMITIVE_FLAG)) {
+ return ((o != 0) and ((o & FIOBJECT_NUMBER_FLAG) == @as(c_int, 0))) and ((o & FIOBJECT_PRIMITIVE_FLAG) != FIOBJECT_PRIMITIVE_FLAG);
+}
+pub inline fn FIOBJ2PTR(o: anytype) ?*anyopaque {
+ return @import("std").zig.c_translation.cast(?*anyopaque, o & FIOBJECT_TYPE_MASK);
+}
+pub inline fn FIOBJECT2VTBL(o: anytype) @TypeOf(fiobj_type_vtable(o)) {
+ return fiobj_type_vtable(o);
+}
+pub inline fn FIOBJECT2HEAD(o: anytype) [*c]fiobj_object_header_s {
+ return @import("std").zig.c_translation.cast([*c]fiobj_object_header_s, FIOBJ2PTR(o));
+}
+pub inline fn fiobj_ary_entry(a: anytype, p: anytype) @TypeOf(fiobj_ary_index(a, p)) {
+ return fiobj_ary_index(a, p);
+}
+pub const FIOBJ_T_NUMBER: c_int = 1;
+pub const FIOBJ_T_NULL: c_int = 6;
+pub const FIOBJ_T_TRUE: c_int = 22;
+pub const FIOBJ_T_FALSE: c_int = 38;
+pub const FIOBJ_T_FLOAT: c_int = 39;
+pub const FIOBJ_T_STRING: c_int = 40;
+pub const FIOBJ_T_ARRAY: c_int = 41;
+pub const FIOBJ_T_HASH: c_int = 42;
+pub const FIOBJ_T_DATA: c_int = 43;
+pub const FIOBJ_T_UNKNOWN: c_int = 44;
+pub const fiobj_type_enum = u8;
+pub const fiobj_object_vtable_s = extern struct {
+ class_name: [*c]const u8,
+ dealloc: ?*const fn (FIOBJ, ?*const fn (FIOBJ, ?*anyopaque) callconv(.C) void, ?*anyopaque) callconv(.C) void,
+ count: ?*const fn (FIOBJ) callconv(.C) usize,
+ is_true: ?*const fn (FIOBJ) callconv(.C) usize,
+ is_eq: ?*const fn (FIOBJ, FIOBJ) callconv(.C) usize,
+ each: ?*const fn (FIOBJ, usize, ?*const fn (FIOBJ, ?*anyopaque) callconv(.C) c_int, ?*anyopaque) callconv(.C) usize,
+ to_str: ?*const fn (FIOBJ) callconv(.C) fio_str_info_s,
+ to_i: ?*const fn (FIOBJ) callconv(.C) isize,
+ to_f: ?*const fn (FIOBJ) callconv(.C) f64,
+};
+pub const fiobj_object_header_s = extern struct {
+ type: fiobj_type_enum,
+ ref: u32,
+};
+pub fn fiobj_type_is(arg_o: FIOBJ, arg_type: fiobj_type_enum) callconv(.C) usize {
+ const o = arg_o;
+ const @"type" = arg_type;
+ while (true) {
+ switch (@as(c_int, @bitCast(@as(c_uint, @"type")))) {
+ @as(c_int, 1) => return @as(usize, @bitCast(@as(c_long, @intFromBool(((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 1))))) != 0) or (@as(c_int, @bitCast(@as(c_uint, @as([*c]fiobj_type_enum, @ptrFromInt(o))[@as(c_uint, @intCast(@as(c_int, 0)))]))) == FIOBJ_T_NUMBER))))),
+ @as(c_int, 6) => return @as(usize, @bitCast(@as(c_long, @intFromBool(!(o != 0) or (o == fiobj_null()))))),
+ @as(c_int, 22) => return @as(usize, @bitCast(@as(c_long, @intFromBool(o == fiobj_true())))),
+ @as(c_int, 38) => return @as(usize, @bitCast(@as(c_long, @intFromBool(o == fiobj_false())))),
+ @as(c_int, 40) => return @as(usize, @bitCast(@as(c_long, @intFromBool(((true and ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 1))))) == @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 0)))))) and ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 6))))) == @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 2)))))) or (((@as(c_int, 2) == @as(c_int, 0)) and (((o != 0) and ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 1))))) == @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 0)))))) and ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 6))))) != @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 6))))))) and (@as(c_int, @bitCast(@as(c_uint, @as([*c]fiobj_type_enum, @ptrCast(@alignCast(@as(?*anyopaque, @ptrFromInt(o & ~@as(usize, @bitCast(@as(c_long, @as(c_int, 7)))))))))[@as(c_uint, @intCast(@as(c_int, 0)))]))) == FIOBJ_T_STRING)))))),
+ @as(c_int, 42) => {
+ if (true) {
+ return @as(usize, @bitCast(@as(c_long, @intFromBool(((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 1))))) == @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 0))))) and ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 6))))) == @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 4)))))))));
+ }
+ return @as(usize, @bitCast(@as(c_long, @intFromBool((((o != 0) and ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 1))))) == @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 0)))))) and ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 6))))) != @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 6)))))) and (@as(c_int, @bitCast(@as(c_uint, @as([*c]fiobj_type_enum, @ptrCast(@alignCast(@as(?*anyopaque, @ptrFromInt(o & ~@as(usize, @bitCast(@as(c_long, @as(c_int, 7)))))))))[@as(c_uint, @intCast(@as(c_int, 0)))]))) == @as(c_int, @bitCast(@as(c_uint, @"type"))))))));
+ },
+ @as(c_int, 39), @as(c_int, 41), @as(c_int, 43), @as(c_int, 44) => return @as(usize, @bitCast(@as(c_long, @intFromBool((((o != 0) and ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 1))))) == @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 0)))))) and ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 6))))) != @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 6)))))) and (@as(c_int, @bitCast(@as(c_uint, @as([*c]fiobj_type_enum, @ptrCast(@alignCast(@as(?*anyopaque, @ptrFromInt(o & ~@as(usize, @bitCast(@as(c_long, @as(c_int, 7)))))))))[@as(c_uint, @intCast(@as(c_int, 0)))]))) == @as(c_int, @bitCast(@as(c_uint, @"type")))))))),
+ else => {},
+ }
+ break;
+ }
+ return @as(usize, @bitCast(@as(c_long, @intFromBool((((o != 0) and ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 1))))) == @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 0)))))) and ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 6))))) != @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 6)))))) and (@as(c_int, @bitCast(@as(c_uint, @as([*c]fiobj_type_enum, @ptrCast(@alignCast(@as(?*anyopaque, @ptrFromInt(o & ~@as(usize, @bitCast(@as(c_long, @as(c_int, 7)))))))))[@as(c_uint, @intCast(@as(c_int, 0)))]))) == @as(c_int, @bitCast(@as(c_uint, @"type"))))))));
+}
+pub fn fiobj_type(arg_o: FIOBJ) callconv(.C) fiobj_type_enum {
+ const o = arg_o;
+ if (!(o != 0)) return @as(u8, @bitCast(@as(i8, @truncate(FIOBJ_T_NULL))));
+ if ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 1))))) != 0) return @as(u8, @bitCast(@as(i8, @truncate(FIOBJ_T_NUMBER))));
+ if ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 6))))) == @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 6))))) return @as(u8, @bitCast(@as(u8, @truncate(o))));
+ if (true and ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 6))))) == @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 2)))))) return @as(u8, @bitCast(@as(i8, @truncate(FIOBJ_T_STRING))));
+ if (true and ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 6))))) == @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 4)))))) return @as(u8, @bitCast(@as(i8, @truncate(FIOBJ_T_HASH))));
+ return @as([*c]fiobj_type_enum, @ptrCast(@alignCast(@as(?*anyopaque, @ptrFromInt(o & ~@as(usize, @bitCast(@as(c_long, @as(c_int, 7)))))))))[@as(c_uint, @intCast(@as(c_int, 0)))];
+}
+pub extern const FIOBJECT_VTABLE_NUMBER: fiobj_object_vtable_s;
+pub extern const FIOBJECT_VTABLE_FLOAT: fiobj_object_vtable_s;
+pub extern const FIOBJECT_VTABLE_STRING: fiobj_object_vtable_s;
+pub extern const FIOBJECT_VTABLE_ARRAY: fiobj_object_vtable_s;
+pub extern const FIOBJECT_VTABLE_HASH: fiobj_object_vtable_s;
+pub extern const FIOBJECT_VTABLE_DATA: fiobj_object_vtable_s;
+pub fn fiobj_type_vtable(arg_o: FIOBJ) callconv(.C) [*c]const fiobj_object_vtable_s {
+ const o = arg_o;
+ while (true) {
+ switch (@as(c_int, @bitCast(@as(c_uint, fiobj_type(o))))) {
+ @as(c_int, 1) => return &FIOBJECT_VTABLE_NUMBER,
+ @as(c_int, 39) => return &FIOBJECT_VTABLE_FLOAT,
+ @as(c_int, 40) => return &FIOBJECT_VTABLE_STRING,
+ @as(c_int, 41) => return &FIOBJECT_VTABLE_ARRAY,
+ @as(c_int, 42) => return &FIOBJECT_VTABLE_HASH,
+ @as(c_int, 43) => return &FIOBJECT_VTABLE_DATA,
+ @as(c_int, 6), @as(c_int, 22), @as(c_int, 38), @as(c_int, 44) => return null,
+ else => {},
+ }
+ break;
+ }
+ return null;
+}
+
+pub fn fiobj_obj2num(o: FIOBJ) callconv(.C) isize {
+ if ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 1))))) != 0) {
+ const sign: usize = if ((o & ~(~@as(usize, @bitCast(@as(c_long, @as(c_int, 0)))) >> @as(@import("std").math.Log2Int(usize), @intCast(1)))) != 0) ~(~@as(usize, @bitCast(@as(c_long, @as(c_int, 0)))) >> @as(@import("std").math.Log2Int(usize), @intCast(1))) | (~(~@as(usize, @bitCast(@as(c_long, @as(c_int, 0)))) >> @as(@import("std").math.Log2Int(usize), @intCast(1))) >> @as(@import("std").math.Log2Int(usize), @intCast(1))) else @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 0))));
+ return @as(isize, @bitCast(((o & (~@as(usize, @bitCast(@as(c_long, @as(c_int, 0)))) >> @as(@import("std").math.Log2Int(usize), @intCast(1)))) >> @as(@import("std").math.Log2Int(c_ulong), @intCast(1))) | sign));
+ }
+ if (!(o != 0) or !(((o != 0) and ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 1))))) == @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 0)))))) and ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 6))))) != @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 6))))))) return @as(isize, @bitCast(@as(c_long, @intFromBool(o == @as(c_ulong, @bitCast(@as(c_long, FIOBJ_T_TRUE)))))));
+ return fiobj_type_vtable(o).*.to_i.?(o);
+}
+pub fn fiobj_obj2float(o: FIOBJ) callconv(.C) f64 {
+ if ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 1))))) != 0) return @as(f64, @floatFromInt(fiobj_obj2num(o)));
+
+
+
+
+ return fiobj_type_vtable(o).*.to_f.?(o);
+}
+
+pub extern fn fio_ltocstr(c_long) fio_str_info_s;
+pub fn fiobj_obj2cstr(o: FIOBJ) callconv(.C) fio_str_info_s {
+ if (!(o != 0)) {
+ const ret: fio_str_info_s = fio_str_info_s{
+ .capa = @as(usize, @bitCast(@as(c_long, @as(c_int, 0)))),
+ .len = @as(usize, @bitCast(@as(c_long, @as(c_int, 4)))),
+ .data = @as([*c]u8, @ptrFromInt(@intFromPtr("null"))),
+ };
+ return ret;
+ }
+ if ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 1))))) != 0) return fio_ltocstr(@as(isize, @bitCast(o)) >> @as(@import("std").math.Log2Int(isize), @intCast(1)));
+ if ((o & @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 6))))) == @as(c_ulong, @bitCast(@as(c_long, @as(c_int, 6))))) {
+ while (true) {
+ switch (@as(c_int, @bitCast(@as(c_uint, @as(u8, @bitCast(@as(u8, @truncate(o)))))))) {
+ @as(c_int, 6) => {
+ {
+ const ret: fio_str_info_s = fio_str_info_s{
+ .capa = @as(usize, @bitCast(@as(c_long, @as(c_int, 0)))),
+ .len = @as(usize, @bitCast(@as(c_long, @as(c_int, 4)))),
+ .data = @as([*c]u8, @ptrFromInt(@intFromPtr("null"))),
+ };
+ return ret;
+ }
+ },
+ @as(c_int, 38) => {
+ {
+ const ret: fio_str_info_s = fio_str_info_s{
+ .capa = @as(usize, @bitCast(@as(c_long, @as(c_int, 0)))),
+ .len = @as(usize, @bitCast(@as(c_long, @as(c_int, 5)))),
+ .data = @as([*c]u8, @ptrFromInt(@intFromPtr("false"))),
+ };
+ return ret;
+ }
+ },
+ @as(c_int, 22) => {
+ {
+ const ret: fio_str_info_s = fio_str_info_s{
+ .capa = @as(usize, @bitCast(@as(c_long, @as(c_int, 0)))),
+ .len = @as(usize, @bitCast(@as(c_long, @as(c_int, 4)))),
+ .data = @as([*c]u8, @ptrFromInt(@intFromPtr("true"))),
+ };
+ return ret;
+ }
+ },
+ else => break,
+ }
+ break;
+ }
+ }
+ return fiobj_type_vtable(o).*.to_str.?(o);
+}
+
+
+
+pub extern fn http_set_header(h: [*c]http_s, name: FIOBJ, value: FIOBJ) c_int;
+pub extern fn http_set_header2(h: [*c]http_s, name: fio_str_info_s, value: fio_str_info_s) c_int;
+pub extern fn http_set_cookie(h: [*c]http_s, http_cookie_args_s) c_int;
+pub extern fn http_sendfile(h: [*c]http_s, fd: c_int, length: usize, offset: usize) c_int;
+pub extern fn http_sendfile2(h: [*c]http_s, prefix: [*c]const u8, prefix_len: usize, encoded: [*c]const u8, encoded_len: usize) c_int;
+pub extern fn http_send_error(h: [*c]http_s, error_code: usize) c_int;
+pub extern fn http_finish(h: [*c]http_s) void;
+pub extern fn http_push_data(h: [*c]http_s, data: ?*anyopaque, length: usize, mime_type: FIOBJ) c_int;
+pub extern fn http_push_file(h: [*c]http_s, filename: FIOBJ, mime_type: FIOBJ) c_int;
+pub const struct_http_pause_handle_s = opaque {};
+pub const http_pause_handle_s = struct_http_pause_handle_s;
+pub extern fn http_pause(h: [*c]http_s, task: ?*const fn (?*http_pause_handle_s) callconv(.C) void) void;
+pub extern fn http_resume(http: ?*http_pause_handle_s, task: ?*const fn ([*c]http_s) callconv(.C) void, fallback: ?*const fn (?*anyopaque) callconv(.C) void) void;
+pub extern fn http_paused_udata_get(http: ?*http_pause_handle_s) ?*anyopaque;
+pub extern fn http_paused_udata_set(http: ?*http_pause_handle_s, udata: ?*anyopaque) ?*anyopaque;
+pub extern fn http_listen(port: [*c]const u8, binding: [*c]const u8, struct_http_settings_s) isize;
+pub extern fn http_connect(url: [*c]const u8, unix_address: [*c]const u8, struct_http_settings_s) isize;
+pub extern fn http_settings(h: [*c]http_s) [*c]struct_http_settings_s;
+pub extern fn http_peer_addr(h: [*c]http_s) fio_str_info_s;
+pub extern fn http_hijack(h: [*c]http_s, leftover: [*c]fio_str_info_s) isize;
+pub const struct_ws_s = opaque {};
+pub const ws_s = struct_ws_s;
+pub const websocket_settings_s = extern struct {
+ on_message: ?*const fn (?*ws_s, fio_str_info_s, u8) callconv(.C) void,
+ on_open: ?*const fn (?*ws_s) callconv(.C) void,
+ on_ready: ?*const fn (?*ws_s) callconv(.C) void,
+ on_shutdown: ?*const fn (?*ws_s) callconv(.C) void,
+ on_close: ?*const fn (isize, ?*anyopaque) callconv(.C) void,
+ udata: ?*anyopaque,
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+pub const websocket_subscribe_s_zigcompat = extern struct {
+ ws: ?*ws_s,
+ channel: fio_str_info_s,
+ on_message: ?*const fn (?*ws_s, fio_str_info_s, fio_str_info_s, ?*anyopaque) callconv(.C) void,
+ on_unsubscribe: ?*const fn (?*anyopaque) callconv(.C) void,
+ udata: ?*anyopaque,
+ match: fio_match_fn,
+ force_binary: u8,
+ force_text: u8,
+};
+
+
+pub extern fn websocket_subscribe_zigcompat(websocket_subscribe_s_zigcompat) callconv(.C) usize;
+
+pub extern fn http_upgrade2ws(http: [*c]http_s, websocket_settings_s) c_int;
+pub extern fn websocket_connect(url: [*c]const u8, settings: websocket_settings_s) c_int;
+pub extern fn websocket_attach(uuid: isize, http_settings: [*c]http_settings_s, args: [*c]websocket_settings_s, data: ?*anyopaque, length: usize) void;
+pub extern fn websocket_udata_get(ws: ?*ws_s) ?*anyopaque;
+pub extern fn websocket_udata_set(ws: ?*ws_s, udata: ?*anyopaque) ?*anyopaque;
+pub extern fn websocket_uuid(ws: ?*ws_s) isize;
+pub extern fn websocket_is_client(ws: ?*ws_s) u8;
+pub extern fn websocket_write(ws: ?*ws_s, msg: fio_str_info_s, is_text: u8) c_int;
+pub extern fn websocket_close(ws: ?*ws_s) void;
+
+pub const struct_websocket_subscribe_s = opaque {};
+pub extern fn websocket_subscribe(args: struct_websocket_subscribe_s) usize;
+pub extern fn websocket_unsubscribe(ws: ?*ws_s, subscription_id: usize) void;
+pub extern fn websocket_optimize4broadcasts(@"type": isize, enable: c_int) void;
+
+pub extern fn fio_publish(args: fio_publish_args_s) void;
+pub const fio_publish_args_s = struct_fio_publish_args_s;
+pub const struct_fio_publish_args_s = extern struct {
+ engine: ?*anyopaque = null,
+
+
+
+
+ filter: i32 = 0,
+ channel: fio_str_info_s,
+ message: fio_str_info_s,
+ is_json: u8,
+};
+
+pub const http_sse_s = struct_http_sse_s;
+pub const struct_http_sse_s = extern struct {
+ on_open: ?*const fn ([*c]http_sse_s) callconv(.C) void,
+ on_ready: ?*const fn ([*c]http_sse_s) callconv(.C) void,
+ on_shutdown: ?*const fn ([*c]http_sse_s) callconv(.C) void,
+ on_close: ?*const fn ([*c]http_sse_s) callconv(.C) void,
+ udata: ?*anyopaque,
+};
+pub extern fn http_upgrade2sse(h: [*c]http_s, http_sse_s) c_int;
+pub extern fn http_sse_set_timout(sse: [*c]http_sse_s, timeout: u8) void;
+pub const fio_match_fn = ?*const fn (fio_str_info_s, fio_str_info_s) callconv(.C) c_int;
+pub const struct_http_sse_subscribe_args = extern struct {
+ channel: fio_str_info_s,
+ on_message: ?*const fn ([*c]http_sse_s, fio_str_info_s, fio_str_info_s, ?*anyopaque) callconv(.C) void,
+ on_unsubscribe: ?*const fn (?*anyopaque) callconv(.C) void,
+ udata: ?*anyopaque,
+ match: fio_match_fn,
+};
+pub extern fn http_sse_subscribe(sse: [*c]http_sse_s, args: struct_http_sse_subscribe_args) usize;
+pub extern fn http_sse_unsubscribe(sse: [*c]http_sse_s, subscription: usize) void;
+pub const struct_http_sse_write_args = extern struct {
+ id: fio_str_info_s,
+ event: fio_str_info_s,
+ data: fio_str_info_s,
+ retry: isize,
+};
+pub extern fn http_sse_write(sse: [*c]http_sse_s, struct_http_sse_write_args) c_int;
+pub extern fn http_sse2uuid(sse: [*c]http_sse_s) isize;
+pub extern fn http_sse_close(sse: [*c]http_sse_s) c_int;
+pub extern fn http_sse_dup(sse: [*c]http_sse_s) [*c]http_sse_s;
+pub extern fn http_sse_free(sse: [*c]http_sse_s) void;
+pub extern fn http_parse_body(h: [*c]http_s) c_int;
+pub extern fn http_parse_query(h: [*c]http_s) void;
+pub extern fn http_parse_cookies(h: [*c]http_s, is_url_encoded: u8) void;
+pub extern fn http_add2hash(dest: FIOBJ, name: [*c]u8, name_len: usize, value: [*c]u8, value_len: usize, encoded: u8) c_int;
+pub extern fn http_add2hash2(dest: FIOBJ, name: [*c]u8, name_len: usize, value: FIOBJ, encoded: u8) c_int;
+pub extern fn http_status2str(status: usize) fio_str_info_s;
+pub extern fn http_mimetype_register(file_ext: [*c]u8, file_ext_len: usize, mime_type_str: FIOBJ) void;
+pub extern fn http_mimetype_find(file_ext: [*c]u8, file_ext_len: usize) FIOBJ;
+pub extern fn http_mimetype_find2(url: FIOBJ) FIOBJ;
+pub extern fn http_mimetype_clear() void;
+pub extern var HTTP_HEADER_ACCEPT: FIOBJ;
+pub extern var HTTP_HEADER_CACHE_CONTROL: FIOBJ;
+pub extern var HTTP_HEADER_CONNECTION: FIOBJ;
+pub extern var HTTP_HEADER_CONTENT_ENCODING: FIOBJ;
+pub extern var HTTP_HEADER_CONTENT_LENGTH: FIOBJ;
+pub extern var HTTP_HEADER_CONTENT_RANGE: FIOBJ;
+pub extern var HTTP_HEADER_CONTENT_TYPE: FIOBJ;
+pub extern var HTTP_HEADER_COOKIE: FIOBJ;
+pub extern var HTTP_HEADER_DATE: FIOBJ;
+pub extern var HTTP_HEADER_ETAG: FIOBJ;
+pub extern var HTTP_HEADER_HOST: FIOBJ;
+pub extern var HTTP_HEADER_LAST_MODIFIED: FIOBJ;
+pub extern var HTTP_HEADER_ORIGIN: FIOBJ;
+pub extern var HTTP_HEADER_SET_COOKIE: FIOBJ;
+pub extern var HTTP_HEADER_UPGRADE: FIOBJ;
+pub extern fn http_req2str(h: [*c]http_s) FIOBJ;
+pub extern fn http_write_log(h: [*c]http_s) void;
+pub extern fn http_gmtime(timer: time_t, tmbuf: [*c]struct_tm) [*c]struct_tm;
+pub extern fn http_date2rfc7231(target: [*c]u8, tmbuf: [*c]struct_tm) usize;
+pub extern fn http_date2rfc2109(target: [*c]u8, tmbuf: [*c]struct_tm) usize;
+pub extern fn http_date2rfc2822(target: [*c]u8, tmbuf: [*c]struct_tm) usize;
+pub fn http_date2str(arg_target: [*c]u8, arg_tmbuf: [*c]struct_tm) callconv(.C) usize {
+ const target = arg_target;
+ const tmbuf = arg_tmbuf;
+ return http_date2rfc7231(target, tmbuf);
+}
+pub extern fn http_time2str(target: [*c]u8, t: time_t) usize;
+pub extern fn http_decode_url_unsafe(dest: [*c]u8, url_data: [*c]const u8) isize;
+pub extern fn http_decode_url(dest: [*c]u8, url_data: [*c]const u8, length: usize) isize;
+pub extern fn http_decode_path_unsafe(dest: [*c]u8, url_data: [*c]const u8) isize;
+pub extern fn http_decode_path(dest: [*c]u8, url_data: [*c]const u8, length: usize) isize;
+pub const http_url_s = fio_url_s;
+
+pub const struct_tm = extern struct {
+ tm_sec: c_int,
+ tm_min: c_int,
+ tm_hour: c_int,
+ tm_mday: c_int,
+ tm_mon: c_int,
+ tm_year: c_int,
+ tm_wday: c_int,
+ tm_yday: c_int,
+ tm_isdst: c_int,
+ tm_gmtoff: c_long,
+ tm_zone: [*c]const u8,
+};
+
+
+
\ No newline at end of file
diff --git a/docs/src/zap/http.zig.html b/docs/src/zap/http.zig.html
new file mode 100644
index 0000000..ffb597d
--- /dev/null
+++ b/docs/src/zap/http.zig.html
@@ -0,0 +1,229 @@
+
+
+
+
+
http.zig - source view
+
+
+
+
+
+
+
+
+pub const StatusCode = enum(u16) {
+
+
+ @"continue" = 100,
+
+
+ switching_protocols = 101,
+ ok = 200,
+ created = 201,
+ accepted = 202,
+ non_authoritative_information = 203,
+ no_content = 204,
+ reset_content = 205,
+
+
+ partial_content = 206,
+ multiple_choices = 300,
+ moved_permanently = 301,
+ found = 302,
+ see_other = 303,
+ not_modified = 304,
+ use_proxy = 305,
+ temporary_redirect = 307,
+
+
+ bad_request = 400,
+ unauthorized = 401,
+ payment_required = 402,
+ forbidden = 403,
+ not_found = 404,
+ method_not_allowed = 405,
+ not_acceptable = 406,
+ proxy_authentication_required = 407,
+ request_timeout = 408,
+ conflict = 409,
+ gone = 410,
+ length_required = 411,
+ precondition_failed = 412,
+ request_entity_too_large = 413,
+ request_uri_too_long = 414,
+ unsupported_mediatype = 415,
+ requested_range_not_satisfiable = 416,
+ expectation_failed = 417,
+
+ teapot = 418,
+ upgrade_required = 426,
+
+ request_header_fields_too_large = 431,
+
+
+ internal_server_error = 500,
+ not_implemented = 501,
+ bad_gateway = 502,
+ service_unavailable = 503,
+ gateway_timeout = 504,
+ http_version_not_supported = 505,
+ _,
+
+
+
+ pub fn toString(self: StatusCode) []const u8 {
+ return switch (self) {
+ .@"continue" => "Continue",
+ .switching_protocols => "Switching Protocols",
+ .ok => "Ok",
+ .created => "Created",
+ .accepted => "Accepted",
+ .non_authoritative_information => "Non Authoritative Information",
+ .no_content => "No Content",
+ .reset_content => "Reset Content",
+ .partial_content => "Partial Content",
+ .multiple_choices => "Multiple Choices",
+ .moved_permanently => "Moved Permanently",
+ .found => "Found",
+ .see_other => "See Other",
+ .not_modified => "Not Modified",
+ .use_proxy => "Use Proxy",
+ .temporary_redirect => "Temporary Redirect",
+ .bad_request => "Bad Request",
+ .unauthorized => "Unauthorized",
+ .payment_required => "Payment Required",
+ .forbidden => "Forbidden",
+ .not_found => "Not Found",
+ .method_not_allowed => "Method Not Allowed",
+ .not_acceptable => "Not Acceptable",
+ .proxy_authentication_required => "Proxy Authentication Required",
+ .request_timeout => "Request Timeout",
+ .conflict => "Conflict",
+ .gone => "Gone",
+ .length_required => "Length Required",
+ .precondition_failed => "Precondition Failed",
+ .request_entity_too_large => "Request Entity Too Large",
+ .request_uri_too_long => "Request-URI Too Long",
+ .unsupported_mediatype => "Unsupported Media Type",
+ .requested_range_not_satisfiable => "Requested Range Not Satisfiable",
+ .teapot => "I'm a Teapot",
+ .upgrade_required => "Upgrade Required",
+ .request_header_fields_too_large => "Request Header Fields Too Large",
+ .expectation_failed => "Expectation Failed",
+ .internal_server_error => "Internal Server Error",
+ .not_implemented => "Not Implemented",
+ .bad_gateway => "Bad Gateway",
+ .service_unavailable => "Service Unavailable",
+ .gateway_timeout => "Gateway Timeout",
+ .http_version_not_supported => "HTTP Version Not Supported",
+ _ => "",
+ };
+ }
+};
+
+
+
\ No newline at end of file
diff --git a/docs/src/zap/http_auth.zig.html b/docs/src/zap/http_auth.zig.html
new file mode 100644
index 0000000..1b6a185
--- /dev/null
+++ b/docs/src/zap/http_auth.zig.html
@@ -0,0 +1,766 @@
+
+
+
+
+
http_auth.zig - source view
+
+
+
+
+
+
const std = @import("std");
+const zap = @import("zap.zig");
+
+
+pub const AuthScheme = enum {
+ Basic,
+ Bearer,
+
+ pub fn str(self: AuthScheme) []const u8 {
+ return switch (self) {
+ .Basic => "Basic ",
+ .Bearer => "Bearer ",
+ };
+ }
+
+ pub fn headerFieldStrFio(self: AuthScheme) []const u8 {
+ return switch (self) {
+ .Basic => "authentication",
+ .Bearer => "authorization",
+ };
+ }
+
+ pub fn headerFieldStrHeader(self: AuthScheme) [:0]const u8 {
+ return switch (self) {
+ .Basic => "Authentication",
+ .Bearer => "Authorization",
+ };
+ }
+};
+
+
+pub fn checkAuthHeader(scheme: AuthScheme, auth_header: []const u8) bool {
+ return switch (scheme) {
+ .Basic => |b| std.mem.startsWith(u8, auth_header, b.str()) and auth_header.len > b.str().len,
+ .Bearer => |b| std.mem.startsWith(u8, auth_header, b.str()) and auth_header.len > b.str().len,
+ };
+}
+
+
+pub fn extractAuthHeader(scheme: AuthScheme, r: *const zap.Request) ?[]const u8 {
+ return switch (scheme) {
+ .Basic => |b| r.getHeader(b.headerFieldStrFio()),
+ .Bearer => |b| r.getHeader(b.headerFieldStrFio()),
+ };
+}
+
+
+const BasicAuthStrategy = enum {
+
+ UserPass,
+
+ Token68,
+};
+
+
+pub const AuthResult = enum {
+
+ AuthOK,
+
+ AuthFailed,
+
+
+
+
+
+ Handled,
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+pub fn Basic(comptime Lookup: type, comptime kind: BasicAuthStrategy) type {
+ return struct {
+ allocator: std.mem.Allocator,
+ realm: ?[]const u8,
+ lookup: *Lookup,
+
+ const Self = @This();
+
+
+
+
+
+
+ pub fn init(allocator: std.mem.Allocator, lookup: *Lookup, realm: ?[]const u8) !Self {
+ return .{
+ .allocator = allocator,
+ .lookup = lookup,
+ .realm = if (realm) |the_realm| try allocator.dupe(u8, the_realm) else null,
+ };
+ }
+
+
+ pub fn deinit(self: *Self) void {
+ if (self.realm) |the_realm| {
+ self.allocator.free(the_realm);
+ }
+ }
+
+
+
+ pub fn authenticateUserPass(self: *Self, auth_header: []const u8) AuthResult {
+ zap.debug("AuthenticateUserPass\n", .{});
+ const encoded = auth_header[AuthScheme.Basic.str().len..];
+ const decoder = std.base64.standard.Decoder;
+ var buffer: [0x100]u8 = undefined;
+ if (decoder.calcSizeForSlice(encoded)) |decoded_size| {
+ if (decoded_size >= buffer.len) {
+ zap.debug(
+ "ERROR: UserPassAuth: decoded_size {d} >= buffer.len {d}\n",
+ .{ decoded_size, buffer.len },
+ );
+ return .AuthFailed;
+ }
+ const decoded = buffer[0..decoded_size];
+ decoder.decode(decoded, encoded) catch |err| {
+ zap.debug(
+ "ERROR: UserPassAuth: unable to decode `{s}`: {any}\n",
+ .{ encoded, err },
+ );
+ return .AuthFailed;
+ };
+
+
+
+
+ var it = std.mem.split(u8, decoded, ":");
+ const user = it.next();
+ const pass = it.next();
+ if (user == null or pass == null) {
+ zap.debug(
+ "ERROR: UserPassAuth: user {any} or pass {any} is null\n",
+ .{ user, pass },
+ );
+ return .AuthFailed;
+ }
+
+
+ const actual_pw = self.lookup.*.get(user.?);
+ if (actual_pw) |pw| {
+ const ret = std.mem.eql(u8, pass.?, pw);
+ zap.debug(
+ "INFO: UserPassAuth for user `{s}`: `{s}` == pass `{s}` = {}\n",
+ .{ user.?, pw, pass.?, ret },
+ );
+ return if (ret) .AuthOK else .AuthFailed;
+ } else {
+ zap.debug(
+ "ERROR: UserPassAuth: user `{s}` not found in map of size {d}!\n",
+ .{ user.?, self.lookup.*.count() },
+ );
+ return .AuthFailed;
+ }
+ } else |err| {
+
+
+ zap.debug(
+ "ERROR: UserPassAuth: cannot calc slize size for encoded `{s}`: {any} \n",
+ .{ encoded, err },
+ );
+ return .AuthFailed;
+ }
+ zap.debug("UNREACHABLE\n", .{});
+ return .AuthFailed;
+ }
+
+
+
+ pub fn authenticateToken68(self: *Self, auth_header: []const u8) AuthResult {
+ const token = auth_header[AuthScheme.Basic.str().len..];
+ return if (self.lookup.*.contains(token)) .AuthOK else .AuthFailed;
+ }
+
+
+
+ pub fn authenticate(self: *Self, auth_header: []const u8) AuthResult {
+ zap.debug("AUTHENTICATE\n", .{});
+ switch (kind) {
+ .UserPass => return self.authenticateUserPass(auth_header),
+ .Token68 => return self.authenticateToken68(auth_header),
+ }
+ }
+
+
+
+
+
+ pub fn authenticateRequest(self: *Self, r: *const zap.Request) AuthResult {
+ zap.debug("AUTHENTICATE REQUEST\n", .{});
+ if (extractAuthHeader(.Basic, r)) |auth_header| {
+ zap.debug("Authentication Header found!\n", .{});
+ return self.authenticate(auth_header);
+ } else {
+
+
+ if (extractAuthHeader(.Bearer, r)) |auth_header| {
+ zap.debug("Authorization Header found!\n", .{});
+ return self.authenticate(auth_header);
+ }
+ }
+ zap.debug("NO fitting Auth Header found!\n", .{});
+ return .AuthFailed;
+ }
+ };
+}
+
+
+
+
+
+
+
+
+
+
+
+pub const BearerSingle = struct {
+ allocator: std.mem.Allocator,
+ token: []const u8,
+ realm: ?[]const u8,
+
+ const Self = @This();
+
+
+
+
+ pub fn init(allocator: std.mem.Allocator, token: []const u8, realm: ?[]const u8) !Self {
+ return .{
+ .allocator = allocator,
+ .token = try allocator.dupe(u8, token),
+ .realm = if (realm) |the_realm| try allocator.dupe(u8, the_realm) else null,
+ };
+ }
+
+
+
+ pub fn authenticate(self: *Self, auth_header: []const u8) AuthResult {
+ if (checkAuthHeader(.Bearer, auth_header) == false) {
+ return .AuthFailed;
+ }
+ const token = auth_header[AuthScheme.Bearer.str().len..];
+ return if (std.mem.eql(u8, token, self.token)) .AuthOK else .AuthFailed;
+ }
+
+
+
+
+ pub fn authenticateRequest(self: *Self, r: *const zap.Request) AuthResult {
+ if (extractAuthHeader(.Bearer, r)) |auth_header| {
+ return self.authenticate(auth_header);
+ }
+ return .AuthFailed;
+ }
+
+
+ pub fn deinit(self: *Self) void {
+ if (self.realm) |the_realm| {
+ self.allocator.free(the_realm);
+ }
+ self.allocator.free(self.token);
+ }
+};
+
+
+
+
+
+
+
+
+
+
+
+pub fn BearerMulti(comptime Lookup: type) type {
+ return struct {
+ allocator: std.mem.Allocator,
+ lookup: *Lookup,
+ realm: ?[]const u8,
+
+ const Self = @This();
+
+
+
+
+ pub fn init(allocator: std.mem.Allocator, lookup: *Lookup, realm: ?[]const u8) !Self {
+ return .{
+ .allocator = allocator,
+ .lookup = lookup,
+ .realm = if (realm) |the_realm| try allocator.dupe(u8, the_realm) else null,
+ };
+ }
+
+
+
+ pub fn deinit(self: *Self) void {
+ if (self.realm) |the_realm| {
+ self.allocator.free(the_realm);
+ }
+ }
+
+
+
+ pub fn authenticate(self: *Self, auth_header: []const u8) AuthResult {
+ if (checkAuthHeader(.Bearer, auth_header) == false) {
+ return .AuthFailed;
+ }
+ const token = auth_header[AuthScheme.Bearer.str().len..];
+ return if (self.lookup.*.contains(token)) .AuthOK else .AuthFailed;
+ }
+
+
+
+
+ pub fn authenticateRequest(self: *Self, r: *const zap.Request) AuthResult {
+ if (extractAuthHeader(.Bearer, r)) |auth_header| {
+ return self.authenticate(auth_header);
+ }
+ return .AuthFailed;
+ }
+ };
+}
+
+
+pub const UserPassSessionArgs = struct {
+
+ usernameParam: []const u8,
+
+ passwordParam: []const u8,
+
+ loginPage: []const u8,
+
+ cookieName: []const u8,
+
+ cookieMaxAge: u8 = 0,
+
+ redirectCode: zap.StatusCode = .found,
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+pub fn UserPassSession(comptime Lookup: type, comptime lockedPwLookups: bool) type {
+ return struct {
+ allocator: std.mem.Allocator,
+ lookup: *Lookup,
+ settings: UserPassSessionArgs,
+
+
+
+ sessionTokens: SessionTokenMap,
+ passwordLookupLock: std.Thread.Mutex = .{},
+ tokenLookupLock: std.Thread.Mutex = .{},
+
+ const Self = @This();
+ const SessionTokenMap = std.StringHashMap(void);
+ const Hash = std.crypto.hash.sha2.Sha256;
+
+ const Token = [Hash.digest_length * 2]u8;
+
+
+
+ pub fn init(
+ allocator: std.mem.Allocator,
+ lookup: *Lookup,
+ args: UserPassSessionArgs,
+ ) !Self {
+ const ret: Self = .{
+ .allocator = allocator,
+ .settings = .{
+ .usernameParam = try allocator.dupe(u8, args.usernameParam),
+ .passwordParam = try allocator.dupe(u8, args.passwordParam),
+ .loginPage = try allocator.dupe(u8, args.loginPage),
+ .cookieName = try allocator.dupe(u8, args.cookieName),
+ .cookieMaxAge = args.cookieMaxAge,
+ .redirectCode = args.redirectCode,
+ },
+ .lookup = lookup,
+ .sessionTokens = SessionTokenMap.init(allocator),
+ };
+
+ return ret;
+ }
+
+
+ pub fn deinit(self: *Self) void {
+ self.allocator.free(self.settings.usernameParam);
+ self.allocator.free(self.settings.passwordParam);
+ self.allocator.free(self.settings.loginPage);
+ self.allocator.free(self.settings.cookieName);
+
+
+
+ var key_it = self.sessionTokens.keyIterator();
+ while (key_it.next()) |key_ptr| {
+ self.allocator.free(key_ptr.*);
+ }
+ self.sessionTokens.deinit();
+ }
+
+
+ pub fn logout(self: *Self, r: *const zap.Request) void {
+
+
+
+
+ if (r.setCookie(.{
+ .name = self.settings.cookieName,
+ .value = "invalid",
+ .max_age_s = -1,
+ })) {
+ zap.debug("logout ok\n", .{});
+ } else |err| {
+ zap.debug("logout cookie setting failed: {any}\n", .{err});
+ }
+
+ r.parseCookies(false);
+
+
+
+ if (r.getCookieStr(self.allocator, self.settings.cookieName, false)) |maybe_cookie| {
+ if (maybe_cookie) |cookie| {
+ defer cookie.deinit();
+ self.tokenLookupLock.lock();
+ defer self.tokenLookupLock.unlock();
+ if (self.sessionTokens.getKeyPtr(cookie.str)) |keyPtr| {
+ const keySlice = keyPtr.*;
+
+
+ _ = self.sessionTokens.remove(cookie.str);
+
+
+
+
+ self.allocator.free(keySlice);
+ }
+ }
+ } else |err| {
+ zap.debug("unreachable: UserPassSession.logout: {any}", .{err});
+ }
+ }
+
+ fn _internal_authenticateRequest(self: *Self, r: *const zap.Request) AuthResult {
+
+
+ if (r.path) |p| {
+ if (std.mem.startsWith(u8, p, self.settings.loginPage)) {
+ return .AuthOK;
+ }
+ }
+
+
+
+ r.parseBody() catch {
+
+
+
+
+ };
+
+ r.parseCookies(false);
+
+
+
+ if (r.getCookieStr(self.allocator, self.settings.cookieName, false)) |maybe_cookie| {
+ if (maybe_cookie) |cookie| {
+ defer cookie.deinit();
+
+
+ self.tokenLookupLock.lock();
+ defer self.tokenLookupLock.unlock();
+ if (self.sessionTokens.contains(cookie.str)) {
+
+
+ zap.debug("Auth: COOKIE IS OK!!!!: {s}\n", .{cookie.str});
+ return .AuthOK;
+ } else {
+ zap.debug("Auth: COOKIE IS BAD!!!!: {s}\n", .{cookie.str});
+
+
+
+
+
+
+ }
+ }
+ } else |err| {
+ zap.debug("unreachable: could not check for cookie in UserPassSession: {any}", .{err});
+ }
+
+
+
+ if (r.getParamStr(self.allocator, self.settings.usernameParam, false)) |maybe_username| {
+ if (maybe_username) |*username| {
+ defer username.deinit();
+ if (r.getParamStr(self.allocator, self.settings.passwordParam, false)) |maybe_pw| {
+ if (maybe_pw) |*pw| {
+ defer pw.deinit();
+
+
+
+ const correct_pw_optional = brk: {
+ if (lockedPwLookups) {
+ self.passwordLookupLock.lock();
+ defer self.passwordLookupLock.unlock();
+ break :brk self.lookup.*.get(username.str);
+ } else {
+ break :brk self.lookup.*.get(username.str);
+ }
+ };
+ if (correct_pw_optional) |correct_pw| {
+ if (std.mem.eql(u8, pw.str, correct_pw)) {
+
+
+ if (self.createAndStoreSessionToken(username.str, pw.str)) |token| {
+ defer self.allocator.free(token);
+
+
+ if (r.setCookie(.{
+ .name = self.settings.cookieName,
+ .value = token,
+ .max_age_s = self.settings.cookieMaxAge,
+ })) {
+ return .AuthOK;
+ } else |err| {
+ zap.debug("could not set session token: {any}", .{err});
+ }
+ } else |err| {
+ zap.debug("could not create session token: {any}", .{err});
+ }
+
+
+ return .AuthOK;
+ }
+ }
+ }
+ } else |err| {
+ zap.debug("getParamSt() for password failed in UserPassSession: {any}", .{err});
+ return .AuthFailed;
+ }
+ }
+ } else |err| {
+ zap.debug("getParamSt() for user failed in UserPassSession: {any}", .{err});
+ return .AuthFailed;
+ }
+ return .AuthFailed;
+ }
+
+
+
+
+ pub fn authenticateRequest(self: *Self, r: *const zap.Request) AuthResult {
+ switch (self._internal_authenticateRequest(r)) {
+ .AuthOK => {
+
+
+ return .AuthOK;
+ },
+
+
+ .Handled => return .Handled,
+
+
+ .AuthFailed => {
+
+
+ self.redirect(r) catch |err| {
+
+
+ zap.debug("redirect() failed in UserPassSession: {any}", .{err});
+ };
+ return .Handled;
+ },
+ }
+ }
+
+ fn redirect(self: *Self, r: *const zap.Request) !void {
+ try r.redirectTo(self.settings.loginPage, self.settings.redirectCode);
+ }
+
+ fn createSessionToken(self: *Self, username: []const u8, password: []const u8) ![]const u8 {
+ var hasher = Hash.init(.{});
+ hasher.update(username);
+ hasher.update(password);
+ var buf: [16]u8 = undefined;
+ const time_nano = std.time.nanoTimestamp();
+ const timestampHex = try std.fmt.bufPrint(&buf, "{0x}", .{time_nano});
+ hasher.update(timestampHex);
+
+ var digest: [Hash.digest_length]u8 = undefined;
+ hasher.final(&digest);
+ const token: Token = std.fmt.bytesToHex(digest, .lower);
+ const token_str = try self.allocator.dupe(u8, token[0..token.len]);
+ return token_str;
+ }
+
+ fn createAndStoreSessionToken(self: *Self, username: []const u8, password: []const u8) ![]const u8 {
+ const token = try self.createSessionToken(username, password);
+ self.tokenLookupLock.lock();
+ defer self.tokenLookupLock.unlock();
+
+ if (!self.sessionTokens.contains(token)) {
+ try self.sessionTokens.put(try self.allocator.dupe(u8, token), {});
+ }
+ return token;
+ }
+ };
+}
+
+
+
\ No newline at end of file
diff --git a/docs/src/zap/log.zig.html b/docs/src/zap/log.zig.html
new file mode 100644
index 0000000..695bcbd
--- /dev/null
+++ b/docs/src/zap/log.zig.html
@@ -0,0 +1,172 @@
+
+
+
+
+
log.zig - source view
+
+
+
+
+
+
const std = @import("std");
+
+
+
+
+debugOn: bool,
+
+
+const Self = @This();
+
+pub fn init(comptime debug: bool) Self {
+ return .{
+ .debugOn = debug,
+ };
+}
+
+pub fn log(self: *const Self, comptime fmt: []const u8, args: anytype) void {
+ if (self.debugOn) {
+ std.debug.print("[zap] - " ++ fmt, args);
+ }
+}
+
+pub extern const fio_log_level_none: c_int;
+pub extern const fio_log_level_fatal: c_int;
+pub extern const fio_log_level_error: c_int;
+pub extern const fio_log_level_warning: c_int;
+pub extern const fio_log_level_info: c_int;
+pub extern const fio_log_level_debug: c_int;
+pub extern fn fio_set_log_level(level: c_int) void;
+pub extern fn fio_get_log_level() c_int;
+pub extern fn fio_log_print(level: c_int, msg: [*c]const u8) void;
+pub extern fn fio_log_info(msg: [*c]const u8) void;
+pub extern fn fio_log_warning(msg: [*c]const u8) void;
+pub extern fn fio_log_error(msg: [*c]const u8) void;
+pub extern fn fio_log_fatal(msg: [*c]const u8) void;
+pub extern fn fio_log_debug(msg: [*c]const u8) void;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/src/zap/middleware.zig.html b/docs/src/zap/middleware.zig.html
new file mode 100644
index 0000000..be09098
--- /dev/null
+++ b/docs/src/zap/middleware.zig.html
@@ -0,0 +1,307 @@
+
+
+
+
+
middleware.zig - source view
+
+
+
+
+
+
const std = @import("std");
+const zap = @import("zap.zig");
+
+
+
+
+
+
+
+
+
+pub fn Handler(comptime ContextType: anytype) type {
+ return struct {
+ other_handler: ?*Self = null,
+ on_request: ?RequestFn = null,
+
+
+
+ allocator: ?std.mem.Allocator = null,
+
+ pub const RequestFn = *const fn (*Self, zap.Request, *ContextType) bool;
+ const Self = @This();
+
+ pub fn init(on_request: RequestFn, other: ?*Self) Self {
+ return .{
+ .other_handler = other,
+ .on_request = on_request,
+ };
+ }
+
+
+
+
+
+
+
+ pub fn handleOther(self: *Self, r: zap.Request, context: *ContextType) bool {
+
+
+
+
+
+
+
+
+
+
+
+
+ var other_handler_finished = false;
+ if (self.other_handler) |other_handler| {
+ if (other_handler.on_request) |on_request| {
+ other_handler_finished = on_request(other_handler, r, context);
+ }
+ }
+
+
+
+ return other_handler_finished;
+ }
+ };
+}
+
+
+pub fn EndpointHandler(comptime HandlerType: anytype, comptime ContextType: anytype) type {
+ return struct {
+ handler: HandlerType,
+ endpoint: *zap.Endpoint,
+ breakOnFinish: bool,
+
+ const Self = @This();
+
+
+
+
+ pub fn init(endpoint: *zap.Endpoint, other: ?*HandlerType, breakOnFinish: bool) Self {
+ return .{
+ .handler = HandlerType.init(onRequest, other),
+ .endpoint = endpoint,
+ .breakOnFinish = breakOnFinish,
+ };
+ }
+
+
+ pub fn getHandler(self: *Self) *HandlerType {
+ return &self.handler;
+ }
+
+
+
+
+
+
+ pub fn onRequest(handler: *HandlerType, r: zap.Request, context: *ContextType) bool {
+ var self = @fieldParentPtr(Self, "handler", handler);
+ r.setUserContext(context);
+ self.endpoint.onRequest(r);
+
+
+
+ if (r.isFinished() and self.breakOnFinish) {
+ return true;
+ }
+ return self.handler.handleOther(r, context);
+ }
+ };
+}
+
+pub const Error = error{
+
+
+ InitOnRequestIsNotNull,
+};
+
+pub const RequestAllocatorFn = *const fn () std.mem.Allocator;
+
+
+pub fn Listener(comptime ContextType: anytype) type {
+ return struct {
+ listener: zap.HttpListener = undefined,
+ settings: zap.HttpListenerSettings,
+
+
+
+ var handler: ?*Handler(ContextType) = undefined;
+
+
+ var requestAllocator: ?RequestAllocatorFn = null;
+
+ const Self = @This();
+
+
+
+
+ pub fn init(settings: zap.HttpListenerSettings, initial_handler: *Handler(ContextType), request_alloc: ?RequestAllocatorFn) Error!Self {
+
+
+ if (settings.on_request != null) {
+ return Error.InitOnRequestIsNotNull;
+ }
+ requestAllocator = request_alloc;
+ std.debug.assert(requestAllocator != null);
+
+ var ret: Self = .{
+ .settings = settings,
+ };
+
+ ret.settings.on_request = onRequest;
+ ret.listener = zap.HttpListener.init(ret.settings);
+ handler = initial_handler;
+ return ret;
+ }
+
+
+ pub fn listen(self: *Self) !void {
+ try self.listener.listen();
+ }
+
+
+
+
+
+
+
+
+ pub fn onRequest(r: zap.Request) void {
+
+
+ var context: ContextType = .{};
+
+
+
+
+
+ var allocator: ?std.mem.Allocator = null;
+ if (requestAllocator) |foo| {
+ allocator = foo();
+ }
+
+ if (handler) |initial_handler| {
+ initial_handler.allocator = allocator;
+ if (initial_handler.on_request) |on_request| {
+
+
+ _ = on_request(initial_handler, r, &context);
+ }
+ }
+ }
+ };
+}
+
+
+
\ No newline at end of file
diff --git a/docs/src/zap/mustache.zig.html b/docs/src/zap/mustache.zig.html
new file mode 100644
index 0000000..7a28907
--- /dev/null
+++ b/docs/src/zap/mustache.zig.html
@@ -0,0 +1,383 @@
+
+
+
+
+
mustache.zig - source view
+
+
+
+
+
+
const std = @import("std");
+const fio = @import("fio.zig");
+const util = @import("util.zig");
+
+const Self = @This();
+
+const struct_mustache_s = opaque {};
+const mustache_s = struct_mustache_s;
+const enum_mustache_error_en = c_uint;
+const mustache_error_en = enum_mustache_error_en;
+
+extern fn fiobj_mustache_new(args: MustacheLoadArgsFio) ?*mustache_s;
+extern fn fiobj_mustache_build(mustache: ?*mustache_s, data: fio.FIOBJ) fio.FIOBJ;
+extern fn fiobj_mustache_build2(dest: fio.FIOBJ, mustache: ?*mustache_s, data: fio.FIOBJ) fio.FIOBJ;
+extern fn fiobj_mustache_free(mustache: ?*mustache_s) void;
+
+
+pub const MustacheLoadArgs = struct {
+
+ filename: ?[]const u8 = null,
+
+
+ data: ?[]const u8 = null,
+};
+
+
+const MustacheLoadArgsFio = extern struct {
+ filename: [*c]const u8,
+ filename_len: usize,
+ data: [*c]const u8,
+ data_len: usize,
+ err: [*c]mustache_error_en,
+};
+
+
+handle: *mustache_s,
+
+pub const Error = error{
+ MUSTACHE_ERR_TOO_DEEP,
+ MUSTACHE_ERR_CLOSURE_MISMATCH,
+ MUSTACHE_ERR_FILE_NOT_FOUND,
+ MUSTACHE_ERR_FILE_TOO_BIG,
+ MUSTACHE_ERR_FILE_NAME_TOO_LONG,
+ MUSTACHE_ERR_FILE_NAME_TOO_SHORT,
+ MUSTACHE_ERR_EMPTY_TEMPLATE,
+ MUSTACHE_ERR_DELIMITER_TOO_LONG,
+ MUSTACHE_ERR_NAME_TOO_LONG,
+ MUSTACHE_ERR_UNKNOWN,
+ MUSTACHE_ERR_USER_ERROR,
+};
+
+
+
+pub fn init(load_args: MustacheLoadArgs) Error!Self {
+ var err: mustache_error_en = undefined;
+
+ const args: MustacheLoadArgsFio = .{
+ .filename = filn: {
+ if (load_args.filename) |filn| break :filn filn.ptr else break :filn null;
+ },
+ .filename_len = filn_len: {
+ if (load_args.filename) |filn| break :filn_len filn.len else break :filn_len 0;
+ },
+ .data = data: {
+ if (load_args.data) |data| break :data data.ptr else break :data null;
+ },
+ .data_len = data_len: {
+ if (load_args.data) |data| break :data_len data.len else break :data_len 0;
+ },
+ .err = &err,
+ };
+
+ const ret = fiobj_mustache_new(args);
+ switch (err) {
+ 0 => return Self{
+ .handle = ret.?,
+ },
+ 1 => return Error.MUSTACHE_ERR_TOO_DEEP,
+ 2 => return Error.MUSTACHE_ERR_CLOSURE_MISMATCH,
+ 3 => return Error.MUSTACHE_ERR_FILE_NOT_FOUND,
+ 4 => return Error.MUSTACHE_ERR_FILE_TOO_BIG,
+ 5 => return Error.MUSTACHE_ERR_FILE_NAME_TOO_LONG,
+ 6 => return Error.MUSTACHE_ERR_FILE_NAME_TOO_SHORT,
+ 7 => return Error.MUSTACHE_ERR_EMPTY_TEMPLATE,
+ 8 => return Error.MUSTACHE_ERR_DELIMITER_TOO_LONG,
+ 9 => return Error.MUSTACHE_ERR_NAME_TOO_LONG,
+ 10 => return Error.MUSTACHE_ERR_UNKNOWN,
+ 11 => return Error.MUSTACHE_ERR_USER_ERROR,
+ else => return Error.MUSTACHE_ERR_UNKNOWN,
+ }
+ unreachable;
+}
+
+
+
+pub fn fromData(data: []const u8) Error!Self {
+ return Self.init(.{ .data = data });
+}
+
+
+
+pub fn fromFile(filename: []const u8) Error!Self {
+ return Self.init(.{ .filename = filename });
+}
+
+
+pub fn deinit(self: *Self) void {
+ fiobj_mustache_free(self.handle);
+}
+
+
+
+
+
+
+
+
+
+const MustacheBuildResult = struct {
+ fiobj_result: fio.FIOBJ = 0,
+
+
+
+ fiobj_context: fio.FIOBJ = 0,
+
+
+ pub fn deinit(m: *const MustacheBuildResult) void {
+ fio.fiobj_free_wrapped(m.fiobj_result);
+ fio.fiobj_free_wrapped(m.fiobj_context);
+ }
+
+
+ pub fn str(m: *const MustacheBuildResult) ?[]const u8 {
+ return util.fio2str(m.fiobj_result);
+ }
+};
+
+
+
+
+
+
+
+
+
+pub fn build(self: *Self, data: anytype) MustacheBuildResult {
+ const T = @TypeOf(data);
+ if (@typeInfo(T) != .Struct) {
+ @compileError("No struct: '" ++ @typeName(T) ++ "'");
+ }
+
+ var result: MustacheBuildResult = .{};
+
+ result.fiobj_context = fiobjectify(data);
+ result.fiobj_result = fiobj_mustache_build(self.handle, result.fiobj_context);
+ return result;
+}
+
+
+
+fn fiobjectify(
+ value: anytype,
+) fio.FIOBJ {
+ const T = @TypeOf(value);
+ switch (@typeInfo(T)) {
+ .Float, .ComptimeFloat => {
+ return fio.fiobj_float_new(value);
+ },
+ .Int, .ComptimeInt => {
+ return fio.fiobj_num_new_bignum(value);
+ },
+ .Bool => {
+ return if (value) fio.fiobj_true() else fio.fiobj_false();
+ },
+ .Null => {
+ return 0;
+ },
+ .Optional => {
+ if (value) |payload| {
+ return fiobjectify(payload);
+ } else {
+ return fiobjectify(null);
+ }
+ },
+ .Enum => {
+ return fio.fiobj_num_new_bignum(@intFromEnum(value));
+ },
+ .Union => {
+ const info = @typeInfo(T).Union;
+ if (info.tag_type) |UnionTagType| {
+ inline for (info.fields) |u_field| {
+ if (value == @field(UnionTagType, u_field.name)) {
+ return fiobjectify(@field(value, u_field.name));
+ }
+ }
+ } else {
+ @compileError("Unable to fiobjectify untagged union '" ++ @typeName(T) ++ "'");
+ }
+ },
+ .Struct => |S| {
+
+
+ const m = fio.fiobj_hash_new();
+
+
+ inline for (S.fields) |Field| {
+
+
+ if (Field.type == void) continue;
+
+
+
+ const fname = fio.fiobj_str_new(util.toCharPtr(Field.name), Field.name.len);
+
+
+ const v = @field(value, Field.name);
+
+
+ const fvalue = fiobjectify(v);
+
+
+ _ = fio.fiobj_hash_set(m, fname, fvalue);
+ fio.fiobj_free_wrapped(fname);
+ }
+ return m;
+ },
+ .ErrorSet => return fiobjectify(@as([]const u8, @errorName(value))),
+ .Pointer => |ptr_info| switch (ptr_info.size) {
+ .One => switch (@typeInfo(ptr_info.child)) {
+ .Array => {
+ const Slice = []const std.meta.Elem(ptr_info.child);
+ return fiobjectify(@as(Slice, value));
+ },
+ else => {
+
+
+ return fiobjectify(value.*);
+ },
+ },
+
+
+ .Slice => {
+
+
+ if (ptr_info.child == u8 and std.unicode.utf8ValidateSlice(value)) {
+ return fio.fiobj_str_new(util.toCharPtr(value), value.len);
+ }
+
+ const arr = fio.fiobj_ary_new2(value.len);
+ for (value) |x| {
+ const v = fiobjectify(x);
+ fio.fiobj_ary_push(arr, v);
+ }
+ return arr;
+ },
+ else => @compileError("Unable to fiobjectify type '" ++ @typeName(T) ++ "'"),
+ },
+ .Array => return fiobjectify(&value),
+ .Vector => |info| {
+ const array: [info.len]info.child = value;
+ return fiobjectify(&array);
+ },
+ else => @compileError("Unable to fiobjectify type '" ++ @typeName(T) ++ "'"),
+ }
+ unreachable;
+}
+
+
+
\ No newline at end of file
diff --git a/docs/src/zap/request.zig.html b/docs/src/zap/request.zig.html
new file mode 100644
index 0000000..a448cb4
--- /dev/null
+++ b/docs/src/zap/request.zig.html
@@ -0,0 +1,883 @@
+
+
+
+
+
request.zig - source view
+
+
+
+
+
+
const std = @import("std");
+const Log = @import("log.zig");
+const http = @import("http.zig");
+const fio = @import("fio.zig");
+
+const util = @import("util.zig");
+const zap = @import("zap.zig");
+
+pub const HttpError = error{
+ HttpSendBody,
+ HttpSetContentType,
+ HttpSetHeader,
+ HttpParseBody,
+ HttpIterParams,
+ SetCookie,
+ SendFile,
+};
+
+
+
+pub const ContentType = enum {
+ TEXT,
+ HTML,
+ JSON,
+
+
+};
+
+
+pub const HttpParamStrKV = struct {
+ key: util.FreeOrNot,
+ value: util.FreeOrNot,
+ pub fn deinit(self: *@This()) void {
+ self.key.deinit();
+ self.value.deinit();
+ }
+};
+
+
+pub const HttpParamStrKVList = struct {
+ items: []HttpParamStrKV,
+ allocator: std.mem.Allocator,
+ pub fn deinit(self: *@This()) void {
+ for (self.items) |*item| {
+ item.deinit();
+ }
+ self.allocator.free(self.items);
+ }
+};
+
+
+pub const HttpParamKVList = struct {
+ items: []HttpParamKV,
+ allocator: std.mem.Allocator,
+ pub fn deinit(self: *const @This()) void {
+ for (self.items) |*item| {
+ item.deinit();
+ }
+ self.allocator.free(self.items);
+ }
+};
+
+
+pub const HttpParamValueType = enum {
+
+
+ Bool,
+ Int,
+ Float,
+ String,
+ Unsupported,
+ Hash_Binfile,
+ Array_Binfile,
+};
+
+
+pub const HttpParam = union(HttpParamValueType) {
+ Bool: bool,
+ Int: isize,
+ Float: f64,
+
+ String: util.FreeOrNot,
+
+ Unsupported: ?void,
+
+ Hash_Binfile: HttpParamBinaryFile,
+
+ Array_Binfile: std.ArrayList(HttpParamBinaryFile),
+};
+
+
+pub const HttpParamKV = struct {
+ key: util.FreeOrNot,
+ value: ?HttpParam,
+ pub fn deinit(self: *@This()) void {
+ self.key.deinit();
+ if (self.value) |p| {
+ switch (p) {
+ .String => |*s| s.deinit(),
+ else => {},
+ }
+ }
+ }
+};
+
+
+pub const HttpParamBinaryFile = struct {
+
+ data: ?[]const u8 = null,
+
+ mimetype: ?[]const u8 = null,
+
+ filename: ?[]const u8 = null,
+
+
+ pub fn format(value: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) std.os.WriteError!void {
+ const d = value.data orelse "\\0";
+ const m = value.mimetype orelse "null";
+ const f = value.filename orelse "null";
+ return writer.print("<{s} ({s}): {any}>", .{ f, m, d });
+ }
+};
+
+fn parseBinfilesFrom(a: std.mem.Allocator, o: fio.FIOBJ) !HttpParam {
+ const key_name = fio.fiobj_str_new("name", 4);
+ const key_data = fio.fiobj_str_new("data", 4);
+ const key_type = fio.fiobj_str_new("type", 4);
+ defer {
+ fio.fiobj_free_wrapped(key_name);
+ fio.fiobj_free_wrapped(key_data);
+ fio.fiobj_free_wrapped(key_type);
+ }
+
+ if (fio.fiobj_hash_haskey(o, key_data) == 1 and fio.fiobj_hash_haskey(o, key_type) == 1 and fio.fiobj_hash_haskey(o, key_name) == 1) {
+ const filename = fio.fiobj_obj2cstr(fio.fiobj_hash_get(o, key_name));
+ const mimetype = fio.fiobj_obj2cstr(fio.fiobj_hash_get(o, key_type));
+ const data = fio.fiobj_hash_get(o, key_data);
+
+ var data_slice: ?[]const u8 = null;
+
+ switch (fio.fiobj_type(data)) {
+ fio.FIOBJ_T_DATA => {
+ if (fio.is_invalid(data) == 1) {
+ data_slice = "(zap: invalid data)";
+ std.log.warn("WARNING: HTTP param binary file is not a data object\n", .{});
+ } else {
+
+
+ const data_len = fio.fiobj_data_len(data);
+ var data_buf = fio.fiobj_data_read(data, data_len);
+
+ if (data_len < 0) {
+ std.log.warn("WARNING: HTTP param binary file size negative: {d}\n", .{data_len});
+ std.log.warn("FIOBJ_TYPE of data is: {d}\n", .{fio.fiobj_type(data)});
+ } else {
+ if (data_buf.len != data_len) {
+ std.log.warn("WARNING: HTTP param binary file size mismatch: should {d}, is: {d}\n", .{ data_len, data_buf.len });
+ }
+
+ if (data_buf.len > 0) {
+ data_slice = data_buf.data[0..data_buf.len];
+ } else {
+ std.log.warn("WARNING: HTTP param binary file buffer size negative: {d}\n", .{data_buf.len});
+ data_slice = "(zap: invalid data: negative BUFFER size)";
+ }
+ }
+ }
+ },
+ fio.FIOBJ_T_STRING => {
+ const fiostr = fio.fiobj_obj2cstr(data);
+ if (fiostr.len == 0) {
+ data_slice = "(zap: empty string data)";
+ std.log.warn("WARNING: HTTP param binary file has empty string object\n", .{});
+ } else {
+ data_slice = fiostr.data[0..fiostr.len];
+ }
+ },
+ fio.FIOBJ_T_ARRAY => {
+
+
+ const len = fio.fiobj_ary_count(data);
+ const fn_ary = fio.fiobj_hash_get(o, key_name);
+ const mt_ary = fio.fiobj_hash_get(o, key_type);
+
+ if (fio.fiobj_ary_count(fn_ary) == len and fio.fiobj_ary_count(mt_ary) == len) {
+ var i: isize = 0;
+ var ret = std.ArrayList(HttpParamBinaryFile).init(a);
+ while (i < len) : (i += 1) {
+ const file_data_obj = fio.fiobj_ary_entry(data, i);
+ const file_name_obj = fio.fiobj_ary_entry(fn_ary, i);
+ const file_mimetype_obj = fio.fiobj_ary_entry(mt_ary, i);
+ var has_error: bool = false;
+ if (fio.is_invalid(file_data_obj) == 1) {
+ std.log.debug("file data invalid in array", .{});
+ has_error = true;
+ }
+ if (fio.is_invalid(file_name_obj) == 1) {
+ std.log.debug("file name invalid in array", .{});
+ has_error = true;
+ }
+ if (fio.is_invalid(file_mimetype_obj) == 1) {
+ std.log.debug("file mimetype invalid in array", .{});
+ has_error = true;
+ }
+ if (has_error) {
+ return error.Invalid;
+ }
+
+ const file_data = fio.fiobj_obj2cstr(file_data_obj);
+ const file_name = fio.fiobj_obj2cstr(file_name_obj);
+ const file_mimetype = fio.fiobj_obj2cstr(file_mimetype_obj);
+ try ret.append(.{
+ .data = file_data.data[0..file_data.len],
+ .mimetype = file_mimetype.data[0..file_mimetype.len],
+ .filename = file_name.data[0..file_name.len],
+ });
+ }
+ return .{ .Array_Binfile = ret };
+ } else {
+ return error.ArrayLenMismatch;
+ }
+ },
+ else => {
+
+
+ return error.Unsupported;
+ },
+ }
+
+ return .{ .Hash_Binfile = .{
+ .filename = filename.data[0..filename.len],
+ .mimetype = mimetype.data[0..mimetype.len],
+ .data = data_slice,
+ } };
+ } else {
+ return .{ .Hash_Binfile = .{} };
+ }
+}
+
+
+pub fn Fiobj2HttpParam(a: std.mem.Allocator, o: fio.FIOBJ, dupe_string: bool) !?HttpParam {
+ return switch (fio.fiobj_type(o)) {
+ fio.FIOBJ_T_NULL => null,
+ fio.FIOBJ_T_TRUE => .{ .Bool = true },
+ fio.FIOBJ_T_FALSE => .{ .Bool = false },
+ fio.FIOBJ_T_NUMBER => .{ .Int = fio.fiobj_obj2num(o) },
+ fio.FIOBJ_T_FLOAT => .{ .Float = fio.fiobj_obj2float(o) },
+ fio.FIOBJ_T_STRING => .{ .String = try util.fio2strAllocOrNot(a, o, dupe_string) },
+ fio.FIOBJ_T_ARRAY => {
+ return .{ .Unsupported = null };
+ },
+ fio.FIOBJ_T_HASH => {
+ const file = try parseBinfilesFrom(a, o);
+ return file;
+ },
+ else => .{ .Unsupported = null },
+ };
+}
+
+
+pub const CookieArgs = struct {
+ name: []const u8,
+ value: []const u8,
+ domain: ?[]const u8 = null,
+ path: ?[]const u8 = null,
+
+ max_age_s: c_int = 0,
+ secure: bool = true,
+ http_only: bool = true,
+};
+
+path: ?[]const u8,
+query: ?[]const u8,
+body: ?[]const u8,
+method: ?[]const u8,
+h: [*c]fio.http_s,
+
+
+
+
+_user_context: *UserContext,
+
+
+
+
+
+_is_finished_request_global: bool,
+
+
+_is_finished: *bool = undefined,
+
+pub const UserContext = struct {
+ user_context: ?*anyopaque = null,
+};
+
+const Self = @This();
+
+
+
+pub fn markAsFinished(self: *const Self, finished: bool) void {
+
+
+ self._is_finished.* = finished;
+}
+
+
+
+pub fn isFinished(self: *const Self) bool {
+
+
+ return self._is_finished.*;
+}
+
+
+
+
+pub fn setUserContext(self: *const Self, context: *anyopaque) void {
+ self._user_context.*.user_context = context;
+}
+
+
+pub fn getUserContext(self: *const Self, comptime Context: type) ?*Context {
+ if (self._user_context.*.user_context) |ptr| {
+ return @as(*Context, @ptrCast(@alignCast(ptr)));
+ } else {
+ return null;
+ }
+}
+
+
+pub fn sendError(self: *const Self, err: anyerror, errorcode_num: usize) void {
+
+
+ if (self._internal_sendError(err, errorcode_num)) {
+ return;
+ } else |_| {
+ self.sendBody(@errorName(err)) catch return;
+ }
+}
+
+
+pub fn _internal_sendError(self: *const Self, err: anyerror, errorcode_num: usize) !void {
+
+
+
+
+ self.h.*.status = errorcode_num;
+ var buf: [20 * 1024]u8 = undefined;
+ var fba = std.heap.FixedBufferAllocator.init(&buf);
+ var string = std.ArrayList(u8).init(fba.allocator());
+ var writer = string.writer();
+ try writer.print("ERROR: {any}\n\n", .{err});
+
+ const debugInfo = try std.debug.getSelfDebugInfo();
+ const ttyConfig: std.io.tty.Config = .no_color;
+ try std.debug.writeCurrentStackTrace(writer, debugInfo, ttyConfig, null);
+ try self.sendBody(string.items);
+}
+
+
+pub fn sendBody(self: *const Self, body: []const u8) HttpError!void {
+ const ret = fio.http_send_body(self.h, @as(
+ *anyopaque,
+ @ptrFromInt(@intFromPtr(body.ptr)),
+ ), body.len);
+ zap.debug("Request.sendBody(): ret = {}\n", .{ret});
+ if (ret == -1) return error.HttpSendBody;
+ self.markAsFinished(true);
+}
+
+
+pub fn sendJson(self: *const Self, json: []const u8) HttpError!void {
+ if (self.setContentType(.JSON)) {
+ if (fio.http_send_body(self.h, @as(
+ *anyopaque,
+ @ptrFromInt(@intFromPtr(json.ptr)),
+ ), json.len) != 0) return error.HttpSendBody;
+ self.markAsFinished(true);
+ } else |err| return err;
+}
+
+
+pub fn setContentType(self: *const Self, c: ContentType) HttpError!void {
+ const s = switch (c) {
+ .TEXT => "text/plain",
+ .JSON => "application/json",
+ else => "text/html",
+ };
+ zap.debug("setting content-type to {s}\n", .{s});
+ return self.setHeader("content-type", s);
+}
+
+
+pub fn redirectTo(self: *const Self, path: []const u8, code: ?http.StatusCode) HttpError!void {
+ self.setStatus(if (code) |status| status else .found);
+ try self.setHeader("Location", path);
+ try self.sendBody("moved");
+ self.markAsFinished(true);
+}
+
+
+pub fn setContentTypeWithLogger(
+ self: *const Self,
+ c: ContentType,
+ logger: *const Log,
+) HttpError!void {
+ const s = switch (c) {
+ .TEXT => "text/plain",
+ .JSON => "application/json",
+ else => "text/html",
+ };
+ logger.log("setting content-type to {s}\n", .{s});
+ return self.setHeader("content-type", s);
+}
+
+
+pub fn setContentTypeFromPath(self: *const Self) !void {
+ const t = fio.http_mimetype_find2(self.h.*.path);
+ if (fio.is_invalid(t) == 1) return error.HttpSetContentType;
+ const ret = fio.fiobj_hash_set(
+ self.h.*.private_data.out_headers,
+ fio.HTTP_HEADER_CONTENT_TYPE,
+ t,
+ );
+ if (ret == -1) return error.HttpSetContentType;
+}
+
+
+
+
+pub fn setContentTypeFromFilename(self: *const Self, filename: []const u8) !void {
+ const ext = std.fs.path.extension(filename);
+
+ if (ext.len > 1) {
+ const e = ext[1..];
+ const obj = fio.http_mimetype_find(@constCast(e.ptr), e.len);
+
+ if (util.fio2str(obj)) |mime_str| {
+ try self.setHeader("content-type", mime_str);
+ }
+ } else {
+ return error.NoExtensionInFilename;
+ }
+}
+
+
+
+pub fn getHeader(self: *const Self, name: []const u8) ?[]const u8 {
+ const hname = fio.fiobj_str_new(util.toCharPtr(name), name.len);
+ defer fio.fiobj_free_wrapped(hname);
+ return util.fio2str(fio.fiobj_hash_get(self.h.*.headers, hname));
+}
+
+
+pub fn setHeader(self: *const Self, name: []const u8, value: []const u8) HttpError!void {
+ const hname: fio.fio_str_info_s = .{
+ .data = util.toCharPtr(name),
+ .len = name.len,
+ .capa = name.len,
+ };
+
+ zap.debug("setHeader: hname = {s}\n", .{name});
+ const vname: fio.fio_str_info_s = .{
+ .data = util.toCharPtr(value),
+ .len = value.len,
+ .capa = value.len,
+ };
+ zap.debug("setHeader: vname = {s}\n", .{value});
+ const ret = fio.http_set_header2(self.h, hname, vname);
+
+
+
+
+
+ if (ret == -1) {
+ std.debug.print("***************** zap.zig:274\n", .{});
+ }
+ zap.debug("setHeader: ret = {}\n", .{ret});
+
+ if (ret == 0) return;
+ return error.HttpSetHeader;
+}
+
+
+pub fn setStatusNumeric(self: *const Self, status: usize) void {
+ self.h.*.status = status;
+}
+
+
+pub fn setStatus(self: *const Self, status: http.StatusCode) void {
+ self.h.*.status = @as(usize, @intCast(@intFromEnum(status)));
+}
+
+
+
+
+
+
+
+
+
+
+
+
+pub fn sendFile(self: *const Self, file_path: []const u8) !void {
+ if (fio.http_sendfile2(self.h, util.toCharPtr(file_path), file_path.len, null, 0) != 0)
+ return error.SendFile;
+ self.markAsFinished(true);
+}
+
+
+
+
+
+
+
+
+
+pub fn parseBody(self: *const Self) HttpError!void {
+ if (fio.http_parse_body(self.h) == -1) return error.HttpParseBody;
+}
+
+
+
+
+
+
+pub fn parseQuery(self: *const Self) void {
+ fio.http_parse_query(self.h);
+}
+
+
+pub fn parseCookies(self: *const Self, url_encoded: bool) void {
+ fio.http_parse_cookies(self.h, if (url_encoded) 1 else 0);
+}
+
+
+pub fn setCookie(self: *const Self, args: CookieArgs) HttpError!void {
+ const c: fio.http_cookie_args_s = .{
+ .name = util.toCharPtr(args.name),
+ .name_len = @as(isize, @intCast(args.name.len)),
+ .value = util.toCharPtr(args.value),
+ .value_len = @as(isize, @intCast(args.value.len)),
+ .domain = if (args.domain) |p| util.toCharPtr(p) else null,
+ .domain_len = if (args.domain) |p| @as(isize, @intCast(p.len)) else 0,
+ .path = if (args.path) |p| util.toCharPtr(p) else null,
+ .path_len = if (args.path) |p| @as(isize, @intCast(p.len)) else 0,
+ .max_age = args.max_age_s,
+ .secure = if (args.secure) 1 else 0,
+ .http_only = if (args.http_only) 1 else 0,
+ };
+
+
+
+
+
+
+
+
+
+
+
+
+
+ const ret = fio.http_set_cookie(self.h, c);
+ if (ret == -1) {
+ std.log.err("fio.http_set_cookie returned: {}\n", .{ret});
+ return error.SetCookie;
+ }
+}
+
+
+pub fn getCookieStr(self: *const Self, a: std.mem.Allocator, name: []const u8, always_alloc: bool) !?util.FreeOrNot {
+ if (self.h.*.cookies == 0) return null;
+ const key = fio.fiobj_str_new(name.ptr, name.len);
+ defer fio.fiobj_free_wrapped(key);
+ const value = fio.fiobj_hash_get(self.h.*.cookies, key);
+ if (value == fio.FIOBJ_INVALID) {
+ return null;
+ }
+ return try util.fio2strAllocOrNot(a, value, always_alloc);
+}
+
+
+
+
+pub fn getCookiesCount(self: *const Self) isize {
+ if (self.h.*.cookies == 0) return 0;
+ return fio.fiobj_obj2num(self.h.*.cookies);
+}
+
+
+
+
+pub fn getParamCount(self: *const Self) isize {
+ if (self.h.*.params == 0) return 0;
+ return fio.fiobj_obj2num(self.h.*.params);
+}
+
+
+pub fn cookiesToOwnedStrList(self: *const Self, a: std.mem.Allocator, always_alloc: bool) anyerror!HttpParamStrKVList {
+ var params = try std.ArrayList(HttpParamStrKV).initCapacity(a, @as(usize, @intCast(self.getCookiesCount())));
+ var context: _parametersToOwnedStrSliceContext = .{
+ .params = ¶ms,
+ .allocator = a,
+ .always_alloc = always_alloc,
+ };
+ const howmany = fio.fiobj_each1(self.h.*.cookies, 0, _each_nextParamStr, &context);
+ if (howmany != self.getCookiesCount()) {
+ return error.HttpIterParams;
+ }
+ return .{ .items = try params.toOwnedSlice(), .allocator = a };
+}
+
+
+pub fn cookiesToOwnedList(self: *const Self, a: std.mem.Allocator, dupe_strings: bool) !HttpParamKVList {
+ var params = try std.ArrayList(HttpParamKV).initCapacity(a, @as(usize, @intCast(self.getCookiesCount())));
+ var context: _parametersToOwnedSliceContext = .{ .params = ¶ms, .allocator = a, .dupe_strings = dupe_strings };
+ const howmany = fio.fiobj_each1(self.h.*.cookies, 0, _each_nextParam, &context);
+ if (howmany != self.getCookiesCount()) {
+ return error.HttpIterParams;
+ }
+ return .{ .items = try params.toOwnedSlice(), .allocator = a };
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+pub fn parametersToOwnedStrList(self: *const Self, a: std.mem.Allocator, always_alloc: bool) anyerror!HttpParamStrKVList {
+ var params = try std.ArrayList(HttpParamStrKV).initCapacity(a, @as(usize, @intCast(self.getParamCount())));
+ var context: _parametersToOwnedStrSliceContext = .{
+ .params = ¶ms,
+ .allocator = a,
+ .always_alloc = always_alloc,
+ };
+ const howmany = fio.fiobj_each1(self.h.*.params, 0, _each_nextParamStr, &context);
+ if (howmany != self.getParamCount()) {
+ return error.HttpIterParams;
+ }
+ return .{ .items = try params.toOwnedSlice(), .allocator = a };
+}
+
+const _parametersToOwnedStrSliceContext = struct {
+ allocator: std.mem.Allocator,
+ params: *std.ArrayList(HttpParamStrKV),
+ last_error: ?anyerror = null,
+ always_alloc: bool,
+};
+
+fn _each_nextParamStr(fiobj_value: fio.FIOBJ, context: ?*anyopaque) callconv(.C) c_int {
+ const ctx: *_parametersToOwnedStrSliceContext = @as(*_parametersToOwnedStrSliceContext, @ptrCast(@alignCast(context)));
+
+
+ const fiobj_key: fio.FIOBJ = fio.fiobj_hash_key_in_loop();
+ ctx.params.append(.{
+ .key = util.fio2strAllocOrNot(ctx.allocator, fiobj_key, ctx.always_alloc) catch |err| {
+ ctx.last_error = err;
+ return -1;
+ },
+ .value = util.fio2strAllocOrNot(ctx.allocator, fiobj_value, ctx.always_alloc) catch |err| {
+ ctx.last_error = err;
+ return -1;
+ },
+ }) catch |err| {
+
+
+
+
+
+
+ ctx.last_error = err;
+ return -1;
+ };
+ return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+pub fn parametersToOwnedList(self: *const Self, a: std.mem.Allocator, dupe_strings: bool) !HttpParamKVList {
+ var params = try std.ArrayList(HttpParamKV).initCapacity(a, @as(usize, @intCast(self.getParamCount())));
+ var context: _parametersToOwnedSliceContext = .{ .params = ¶ms, .allocator = a, .dupe_strings = dupe_strings };
+ const howmany = fio.fiobj_each1(self.h.*.params, 0, _each_nextParam, &context);
+ if (howmany != self.getParamCount()) {
+ return error.HttpIterParams;
+ }
+ return .{ .items = try params.toOwnedSlice(), .allocator = a };
+}
+
+const _parametersToOwnedSliceContext = struct {
+ params: *std.ArrayList(HttpParamKV),
+ last_error: ?anyerror = null,
+ allocator: std.mem.Allocator,
+ dupe_strings: bool,
+};
+
+fn _each_nextParam(fiobj_value: fio.FIOBJ, context: ?*anyopaque) callconv(.C) c_int {
+ const ctx: *_parametersToOwnedSliceContext = @as(*_parametersToOwnedSliceContext, @ptrCast(@alignCast(context)));
+
+
+ const fiobj_key: fio.FIOBJ = fio.fiobj_hash_key_in_loop();
+ ctx.params.append(.{
+ .key = util.fio2strAllocOrNot(ctx.allocator, fiobj_key, ctx.dupe_strings) catch |err| {
+ ctx.last_error = err;
+ return -1;
+ },
+ .value = Fiobj2HttpParam(ctx.allocator, fiobj_value, ctx.dupe_strings) catch |err| {
+ ctx.last_error = err;
+ return -1;
+ },
+ }) catch |err| {
+
+
+
+
+
+
+ ctx.last_error = err;
+ return -1;
+ };
+ return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+pub fn getParamStr(self: *const Self, a: std.mem.Allocator, name: []const u8, always_alloc: bool) !?util.FreeOrNot {
+ if (self.h.*.params == 0) return null;
+ const key = fio.fiobj_str_new(name.ptr, name.len);
+ defer fio.fiobj_free_wrapped(key);
+ const value = fio.fiobj_hash_get(self.h.*.params, key);
+ if (value == fio.FIOBJ_INVALID) {
+ return null;
+ }
+ return try util.fio2strAllocOrNot(a, value, always_alloc);
+}
+
+
+
\ No newline at end of file
diff --git a/docs/src/zap/tls.zig.html b/docs/src/zap/tls.zig.html
new file mode 100644
index 0000000..7bc80ba
--- /dev/null
+++ b/docs/src/zap/tls.zig.html
@@ -0,0 +1,202 @@
+
+
+
+
+
tls.zig - source view
+
+
+
+
+
+
const fio = @import("fio.zig");
+
+
+const Tls = @This();
+
+fio_tls: ?*anyopaque = null,
+
+
+
+
+pub const TlsSettings = struct {
+
+
+ server_name: ?[*:0]const u8 = null,
+ public_certificate_file: ?[*:0]const u8 = null,
+ private_key_file: ?[*:0]const u8 = null,
+
+ private_key_password: ?[*:0]const u8 = null,
+};
+
+
+
+
+
+
+pub fn init(settings: TlsSettings) !Tls {
+ const ret = fio.fio_tls_new(
+ settings.server_name,
+ settings.public_certificate_file,
+ settings.private_key_file,
+ settings.private_key_password,
+ );
+ if (ret == null) return error.FileNotFound;
+ return Tls{ .fio_tls = ret };
+}
+
+
+pub fn deinit(tls: *const Tls) void {
+ fio.fio_tls_destroy(tls.fio_tls);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+pub fn addCertificate(tls: *Tls, settings: TlsSettings) !void {
+ if (tls.fio_tls == null) {
+ return error.Uninitialized;
+ }
+
+ const ret = fio.fio_tls_cert_add(
+ tls.fio_tls,
+ settings.server_name,
+ settings.public_certificate_file,
+ settings.private_key_file,
+ settings.private_key_password,
+ );
+
+ if (ret != 0) return error.FileNotFound;
+ return;
+}
+
+
+
+
+pub fn trust(tls: *Tls, public_cert_file: [*:0]const u8) !void {
+ if (tls.fio_tls == null) {
+ return error.Uninitialized;
+ }
+
+ const ret = fio.fio_tls_trust(tls.fio_tls, public_cert_file);
+ if (ret != 0) return error.FileNotFound;
+ return;
+}
+
+
+
\ No newline at end of file
diff --git a/docs/src/zap/util.zig.html b/docs/src/zap/util.zig.html
new file mode 100644
index 0000000..4c58d65
--- /dev/null
+++ b/docs/src/zap/util.zig.html
@@ -0,0 +1,208 @@
+
+
+
+
+
util.zig - source view
+
+
+
+
+
+
const std = @import("std");
+const fio = @import("fio.zig");
+
+
+
+
+
+pub fn fio2str(o: fio.FIOBJ) ?[]const u8 {
+ if (o == 0) return null;
+ const x: fio.fio_str_info_s = fio.fiobj_obj2cstr(o);
+ if (x.data == 0)
+ return null;
+
+ return x.data[0..x.len];
+}
+
+
+
+
+
+
+
+pub const FreeOrNot = struct {
+ str: []const u8,
+ freeme: bool,
+ allocator: ?std.mem.Allocator = null,
+
+ pub fn deinit(self: *const @This()) void {
+ if (self.freeme) {
+ self.allocator.?.free(self.str);
+ }
+ }
+};
+
+
+
+
+pub fn fio2strAllocOrNot(a: std.mem.Allocator, o: fio.FIOBJ, always_alloc: bool) !FreeOrNot {
+ if (o == 0) return .{ .str = "null", .freeme = false };
+ if (o == fio.FIOBJ_INVALID) return .{ .str = "invalid", .freeme = false };
+ return switch (fio.fiobj_type(o)) {
+ fio.FIOBJ_T_TRUE => .{ .str = "true", .freeme = false },
+ fio.FIOBJ_T_FALSE => .{ .str = "false", .freeme = false },
+
+
+ fio.FIOBJ_T_NUMBER => .{ .str = try a.dupe(u8, fio2str(o) orelse "null"), .freeme = true, .allocator = a },
+ fio.FIOBJ_T_FLOAT => .{ .str = try a.dupe(u8, fio2str(o) orelse "null"), .freeme = true, .allocator = a },
+
+
+ fio.FIOBJ_T_STRING => .{ .str = if (always_alloc) try a.dupe(u8, fio2str(o) orelse "") else fio2str(o) orelse "", .freeme = if (always_alloc) true else false, .allocator = a },
+ else => .{ .str = "unknown_type", .freeme = false },
+ };
+}
+
+
+pub fn str2fio(s: []const u8) fio.fio_str_info_s {
+ return .{
+ .data = toCharPtr(s),
+ .len = s.len,
+ .capa = s.len,
+ };
+}
+
+
+pub fn toCharPtr(s: []const u8) [*c]u8 {
+ return @as([*c]u8, @ptrFromInt(@intFromPtr(s.ptr)));
+}
+
+
+
+
+
+
+
+
+
+
+pub fn stringifyBuf(
+ buffer: []u8,
+ value: anytype,
+ options: std.json.StringifyOptions,
+) ?[]const u8 {
+ var fba = std.heap.FixedBufferAllocator.init(buffer);
+ var string = std.ArrayList(u8).init(fba.allocator());
+ if (std.json.stringify(value, options, string.writer())) {
+ return string.items;
+ } else |_| {
+
+ return null;
+ }
+}
+
+
+
\ No newline at end of file
diff --git a/docs/src/zap/websockets.zig.html b/docs/src/zap/websockets.zig.html
new file mode 100644
index 0000000..de0db91
--- /dev/null
+++ b/docs/src/zap/websockets.zig.html
@@ -0,0 +1,346 @@
+
+
+
+
+
websockets.zig - source view
+
+
+
+
+
+
const std = @import("std");
+const zap = @import("zap.zig");
+const fio = @import("fio.zig");
+const util = @import("util.zig");
+
+
+pub const WsHandle = ?*fio.ws_s;
+
+
+
+
+pub fn Handler(comptime ContextType: type) type {
+ return struct {
+
+ pub const WsOnMessageFn = *const fn (
+
+ context: ?*ContextType,
+
+ handle: WsHandle,
+
+ message: []const u8,
+
+ is_text: bool,
+ ) void;
+
+
+
+ pub const WsOnCloseFn = *const fn (context: ?*ContextType, uuid: isize) void;
+
+
+
+ pub const WsFn = *const fn (context: ?*ContextType, handle: WsHandle) void;
+
+
+
+ pub const WebSocketSettings = struct {
+
+ on_message: ?WsOnMessageFn = null,
+
+ on_open: ?WsFn = null,
+
+ on_ready: ?WsFn = null,
+
+ on_shutdown: ?WsFn = null,
+
+ on_close: ?WsOnCloseFn = null,
+
+ context: ?*ContextType = null,
+ };
+
+
+ pub fn upgrade(h: [*c]fio.http_s, settings: *WebSocketSettings) WebSocketError!void {
+ const fio_settings: fio.websocket_settings_s = .{
+ .on_message = internal_on_message,
+ .on_open = internal_on_open,
+ .on_ready = internal_on_ready,
+ .on_shutdown = internal_on_shutdown,
+ .on_close = internal_on_close,
+ .udata = settings,
+ };
+ if (fio.http_upgrade2ws(h, fio_settings) != 0) {
+ return error.UpgradeError;
+ }
+ }
+
+ fn internal_on_message(handle: WsHandle, msg: fio.fio_str_info_s, is_text: u8) callconv(.C) void {
+ const user_provided_settings: ?*WebSocketSettings = @as(?*WebSocketSettings, @ptrCast(@alignCast(fio.websocket_udata_get(handle))));
+ const message = msg.data[0..msg.len];
+ if (user_provided_settings) |settings| {
+ if (settings.on_message) |on_message| {
+ on_message(settings.context, handle, message, is_text == 1);
+ }
+ }
+ }
+
+ fn internal_on_open(handle: WsHandle) callconv(.C) void {
+ const user_provided_settings: ?*WebSocketSettings = @as(?*WebSocketSettings, @ptrCast(@alignCast(fio.websocket_udata_get(handle))));
+ if (user_provided_settings) |settings| {
+ if (settings.on_open) |on_open| {
+ on_open(settings.context, handle);
+ }
+ }
+ }
+
+ fn internal_on_ready(handle: WsHandle) callconv(.C) void {
+ const user_provided_settings: ?*WebSocketSettings = @as(?*WebSocketSettings, @ptrCast(@alignCast(fio.websocket_udata_get(handle))));
+ if (user_provided_settings) |settings| {
+ if (settings.on_ready) |on_ready| {
+ on_ready(settings.context, handle);
+ }
+ }
+ }
+
+ fn internal_on_shutdown(handle: WsHandle) callconv(.C) void {
+ const user_provided_settings: ?*WebSocketSettings = @as(?*WebSocketSettings, @ptrCast(@alignCast(fio.websocket_udata_get(handle))));
+ if (user_provided_settings) |settings| {
+ if (settings.on_shutdown) |on_shutdown| {
+ on_shutdown(settings.context, handle);
+ }
+ }
+ }
+
+ fn internal_on_close(uuid: isize, udata: ?*anyopaque) callconv(.C) void {
+ const user_provided_settings: ?*WebSocketSettings = @as(?*WebSocketSettings, @ptrCast(@alignCast(udata)));
+ if (user_provided_settings) |settings| {
+ if (settings.on_close) |on_close| {
+ on_close(settings.context, uuid);
+ }
+ }
+ }
+
+ pub const WebSocketError = error{
+ WriteError,
+ UpgradeError,
+ SubscribeError,
+ };
+
+
+ pub inline fn write(handle: WsHandle, message: []const u8, is_text: bool) WebSocketError!void {
+ if (fio.websocket_write(
+ handle,
+ fio.str2fio(message),
+ if (is_text) 1 else 0,
+ ) != 0) {
+ return error.WriteError;
+ }
+ }
+
+
+
+
+ pub fn udataToContext(udata: *anyopaque) *ContextType {
+ return @as(*ContextType, @ptrCast(@alignCast(udata)));
+ }
+
+
+ pub inline fn close(handle: WsHandle) void {
+ fio.websocket_close(handle);
+ }
+
+
+ const PublishArgs = struct {
+ channel: []const u8,
+ message: []const u8,
+ is_json: bool = false,
+ };
+
+
+ pub inline fn publish(args: PublishArgs) void {
+ fio.fio_publish(.{
+ .channel = util.str2fio(args.channel),
+ .message = util.str2fio(args.message),
+ .is_json = if (args.is_json) 1 else 0,
+ });
+ }
+
+
+ pub const SubscriptionOnMessageFn = *const fn (context: ?*ContextType, handle: WsHandle, channel: []const u8, message: []const u8) void;
+
+
+ pub const SubscriptionOnUnsubscribeFn = *const fn (context: ?*ContextType) void;
+
+
+ pub const SubscribeArgs = struct {
+
+ channel: []const u8,
+
+ on_message: ?SubscriptionOnMessageFn = null,
+
+ on_unsubscribe: ?SubscriptionOnUnsubscribeFn = null,
+
+ match: fio.fio_match_fn = null,
+
+
+
+
+
+ force_binary: bool = false,
+
+
+
+
+
+ force_text: bool = false,
+
+ context: ?*ContextType = null,
+ };
+
+
+
+
+
+ pub inline fn subscribe(handle: WsHandle, args: *SubscribeArgs) WebSocketError!usize {
+ if (handle == null) return error.SubscribeError;
+ const fio_args: fio.websocket_subscribe_s_zigcompat = .{
+ .ws = handle.?,
+ .channel = util.str2fio(args.channel),
+ .on_message = if (args.on_message) |_| internal_subscription_on_message else null,
+ .on_unsubscribe = if (args.on_unsubscribe) |_| internal_subscription_on_unsubscribe else null,
+ .match = args.match,
+ .force_binary = if (args.force_binary) 1 else 0,
+ .force_text = if (args.force_text) 1 else 0,
+ .udata = args,
+ };
+ const ret = fio.websocket_subscribe_zigcompat(fio_args);
+ if (ret == 0) {
+ return error.SubscribeError;
+ }
+ return ret;
+ }
+
+ pub fn internal_subscription_on_message(handle: WsHandle, channel: fio.fio_str_info_s, message: fio.fio_str_info_s, udata: ?*anyopaque) callconv(.C) void {
+ if (udata) |p| {
+ const args = @as(*SubscribeArgs, @ptrCast(@alignCast(p)));
+ if (args.on_message) |on_message| {
+ on_message(args.context, handle, channel.data[0..channel.len], message.data[0..message.len]);
+ }
+ }
+ }
+ pub fn internal_subscription_on_unsubscribe(udata: ?*anyopaque) callconv(.C) void {
+ if (udata) |p| {
+ const args = @as(*SubscribeArgs, @ptrCast(@alignCast(p)));
+ if (args.on_unsubscribe) |on_unsubscribe| {
+ on_unsubscribe(args.context);
+ }
+ }
+ }
+ };
+}
+
+
+
\ No newline at end of file
diff --git a/docs/src/zap/zap.zig.html b/docs/src/zap/zap.zig.html
new file mode 100644
index 0000000..5a05ba4
--- /dev/null
+++ b/docs/src/zap/zap.zig.html
@@ -0,0 +1,548 @@
+
+
+
+
+
zap.zig - source view
+
+
+
+
+
+
+
+
+
+
+const std = @import("std");
+
+
+pub const fio = @import("fio.zig");
+
+
+pub const Tls = @import("tls.zig");
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+pub const Endpoint = @import("endpoint.zig");
+
+pub usingnamespace @import("util.zig");
+pub usingnamespace @import("http.zig");
+
+
+
+
+
+pub const Mustache = @import("mustache.zig");
+
+
+pub const Auth = @import("http_auth.zig");
+
+
+pub const Request = @import("request.zig");
+
+
+
+
+
+
+pub const Middleware = @import("middleware.zig");
+
+
+pub const WebSockets = @import("websockets.zig");
+
+pub const Log = @import("log.zig");
+const http = @import("http.zig");
+
+const util = @import("util.zig");
+
+
+
+var _debug: bool = false;
+
+
+
+
+pub fn start(args: fio.fio_start_args) void {
+ fio.fio_start(args);
+}
+
+
+
+
+
+
+pub fn stop() void {
+ fio.fio_stop();
+}
+
+
+
+pub fn debug(comptime fmt: []const u8, args: anytype) void {
+ if (_debug) {
+ std.debug.print("[zap] - " ++ fmt, args);
+ }
+}
+
+
+pub fn enableDebugLog() void {
+ _debug = true;
+}
+
+
+pub fn startWithLogging(args: fio.fio_start_args) void {
+ debug = true;
+ fio.fio_start(args);
+}
+
+pub const ListenError = error{
+ AlreadyListening,
+ ListenError,
+};
+
+pub const HttpError = error{
+ HttpSendBody,
+ HttpSetContentType,
+ HttpSetHeader,
+ HttpParseBody,
+ HttpIterParams,
+ SetCookie,
+ SendFile,
+};
+
+
+
+pub const ContentType = enum {
+ TEXT,
+ HTML,
+ JSON,
+
+
+};
+
+
+pub const FioHttpRequestFn = *const fn (r: [*c]fio.http_s) callconv(.C) void;
+
+
+pub const HttpRequestFn = *const fn (Request) void;
+
+
+
+pub const HttpUpgradeFn = *const fn (r: Request, target_protocol: []const u8) void;
+
+
+
+pub const HttpFinishSettings = [*c]fio.struct_http_settings_s;
+
+
+pub const HttpFinishFn = *const fn (HttpFinishSettings) void;
+
+
+pub const HttpListenerSettings = struct {
+ port: usize,
+ interface: [*c]const u8 = null,
+ on_request: ?HttpRequestFn,
+ on_response: ?HttpRequestFn = null,
+ on_upgrade: ?HttpUpgradeFn = null,
+ on_finish: ?HttpFinishFn = null,
+
+
+
+
+ udata: ?*anyopaque = null,
+ public_folder: ?[]const u8 = null,
+ max_clients: ?isize = null,
+ max_body_size: ?usize = null,
+ timeout: ?u8 = null,
+ log: bool = false,
+ ws_timeout: u8 = 40,
+ ws_max_msg_size: usize = 262144,
+ tls: ?Tls = null,
+};
+
+
+pub const HttpListener = struct {
+ settings: HttpListenerSettings,
+
+ const Self = @This();
+ var the_one_and_only_listener: ?*HttpListener = null;
+
+
+ pub fn init(settings: HttpListenerSettings) Self {
+ std.debug.assert(settings.on_request != null);
+ return .{
+ .settings = settings,
+ };
+ }
+
+
+
+
+ pub fn theOneAndOnlyRequestCallBack(r: [*c]fio.http_s) callconv(.C) void {
+ if (the_one_and_only_listener) |l| {
+ var req: Request = .{
+ .path = util.fio2str(r.*.path),
+ .query = util.fio2str(r.*.query),
+ .body = util.fio2str(r.*.body),
+ .method = util.fio2str(r.*.method),
+ .h = r,
+ ._is_finished_request_global = false,
+ ._user_context = undefined,
+ };
+ req._is_finished = &req._is_finished_request_global;
+
+ var user_context: Request.UserContext = .{};
+ req._user_context = &user_context;
+
+ req.markAsFinished(false);
+ std.debug.assert(l.settings.on_request != null);
+ if (l.settings.on_request) |on_request| {
+
+
+ on_request(req);
+ }
+ }
+ }
+
+
+ pub fn theOneAndOnlyResponseCallBack(r: [*c]fio.http_s) callconv(.C) void {
+ if (the_one_and_only_listener) |l| {
+ var req: Request = .{
+ .path = util.fio2str(r.*.path),
+ .query = util.fio2str(r.*.query),
+ .body = util.fio2str(r.*.body),
+ .method = util.fio2str(r.*.method),
+ .h = r,
+ ._is_finished_request_global = false,
+ ._user_context = undefined,
+ };
+ req._is_finished = &req._is_finished_request_global;
+
+ var user_context: Request.UserContext = .{};
+ req._user_context = &user_context;
+
+ l.settings.on_response.?(req);
+ }
+ }
+
+
+ pub fn theOneAndOnlyUpgradeCallBack(r: [*c]fio.http_s, target: [*c]u8, target_len: usize) callconv(.C) void {
+ if (the_one_and_only_listener) |l| {
+ var req: Request = .{
+ .path = util.fio2str(r.*.path),
+ .query = util.fio2str(r.*.query),
+ .body = util.fio2str(r.*.body),
+ .method = util.fio2str(r.*.method),
+ .h = r,
+ ._is_finished_request_global = false,
+ ._user_context = undefined,
+ };
+ const zigtarget: []u8 = target[0..target_len];
+ req._is_finished = &req._is_finished_request_global;
+
+ var user_context: Request.UserContext = .{};
+ req._user_context = &user_context;
+
+ l.settings.on_upgrade.?(req, zigtarget);
+ }
+ }
+
+
+ pub fn theOneAndOnlyFinishCallBack(s: [*c]fio.struct_http_settings_s) callconv(.C) void {
+ if (the_one_and_only_listener) |l| {
+ l.settings.on_finish.?(s);
+ }
+ }
+
+
+ pub fn listen(self: *Self) !void {
+ var pfolder: [*c]const u8 = null;
+ var pfolder_len: usize = 0;
+
+ if (self.settings.public_folder) |pf| {
+ debug("HttpListener.listen(): public folder is {s}\n", .{pf});
+ pfolder_len = pf.len;
+ pfolder = pf.ptr;
+ }
+
+ const x: fio.http_settings_s = .{
+ .on_request = if (self.settings.on_request) |_| Self.theOneAndOnlyRequestCallBack else null,
+ .on_upgrade = if (self.settings.on_upgrade) |_| Self.theOneAndOnlyUpgradeCallBack else null,
+ .on_response = if (self.settings.on_response) |_| Self.theOneAndOnlyResponseCallBack else null,
+ .on_finish = if (self.settings.on_finish) |_| Self.theOneAndOnlyFinishCallBack else null,
+ .udata = null,
+ .public_folder = pfolder,
+ .public_folder_length = pfolder_len,
+ .max_header_size = 32 * 1024,
+ .max_body_size = self.settings.max_body_size orelse 50 * 1024 * 1024,
+
+
+ .max_clients = self.settings.max_clients orelse 0,
+ .tls = if (self.settings.tls) |tls| tls.fio_tls else null,
+ .reserved1 = 0,
+ .reserved2 = 0,
+ .reserved3 = 0,
+ .ws_max_msg_size = 0,
+ .timeout = self.settings.timeout orelse 5,
+ .ws_timeout = self.settings.ws_timeout,
+ .log = if (self.settings.log) 1 else 0,
+ .is_client = 0,
+ };
+
+
+
+
+
+
+
+
+ std.time.sleep(500 * std.time.ns_per_ms);
+
+ var portbuf: [100]u8 = undefined;
+ const printed_port = try std.fmt.bufPrintZ(&portbuf, "{d}", .{self.settings.port});
+
+ const ret = fio.http_listen(printed_port.ptr, self.settings.interface, x);
+ if (ret == -1) {
+ return error.ListenError;
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Self.the_one_and_only_listener = self;
+ }
+};
+
+
+pub const LowLevel = struct {
+
+
+ pub const ListenSettings = struct {
+ on_request: ?FioHttpRequestFn = null,
+ on_upgrade: ?FioHttpRequestFn = null,
+ on_response: ?FioHttpRequestFn = null,
+ on_finish: ?FioHttpRequestFn = null,
+ public_folder: ?[]const u8 = null,
+ max_header_size: usize = 32 * 1024,
+ max_body_size: usize = 50 * 1024 * 1024,
+ max_clients: isize = 100,
+ keepalive_timeout_s: u8 = 5,
+ log: bool = false,
+
+ const Self = @This();
+
+
+ pub fn init() Self {
+ return .{};
+ }
+ };
+
+
+ pub fn listen(port: [*c]const u8, interface: [*c]const u8, settings: ListenSettings) ListenError!void {
+ var pfolder: [*c]const u8 = null;
+ var pfolder_len: usize = 0;
+
+ if (settings.public_folder) |pf| {
+ pfolder_len = pf.len;
+ pfolder = pf.ptr;
+ }
+ const x: fio.http_settings_s = .{
+ .on_request = settings.on_request,
+ .on_upgrade = settings.on_upgrade,
+ .on_response = settings.on_response,
+ .on_finish = settings.on_finish,
+ .udata = null,
+ .public_folder = pfolder,
+ .public_folder_length = pfolder_len,
+ .max_header_size = settings.max_header_size,
+ .max_body_size = settings.max_body_size,
+ .max_clients = settings.max_clients,
+ .tls = null,
+ .reserved1 = 0,
+ .reserved2 = 0,
+ .reserved3 = 0,
+ .ws_max_msg_size = settings.ws_max_msg_size,
+ .timeout = settings.keepalive_timeout_s,
+ .ws_timeout = 0,
+ .log = if (settings.log) 1 else 0,
+ .is_client = 0,
+ };
+
+
+
+
+
+
+
+
+ std.time.sleep(500 * std.time.ns_per_ms);
+
+ if (fio.http_listen(port, interface, x) == -1) {
+ return error.ListenError;
+ }
+ }
+
+
+ pub fn sendBody(request: [*c]fio.http_s, body: []const u8) HttpError!void {
+ const ret = fio.http_send_body(request, @as(
+ *anyopaque,
+ @ptrFromInt(@intFromPtr(body.ptr)),
+ ), body.len);
+ debug("sendBody(): ret = {}\n", .{ret});
+ if (ret != -1) return error.HttpSendBody;
+ }
+};
+
+
+
\ No newline at end of file
diff --git a/docs/ziglexer.js b/docs/ziglexer.js
new file mode 100644
index 0000000..fdd94be
--- /dev/null
+++ b/docs/ziglexer.js
@@ -0,0 +1,2147 @@
+'use strict';
+
+const Tag = {
+ whitespace: "whitespace",
+ invalid: "invalid",
+ identifier: "identifier",
+ string_literal: "string_literal",
+ multiline_string_literal_line: "multiline_string_literal_line",
+ char_literal: "char_literal",
+ eof: "eof",
+ builtin: "builtin",
+ number_literal: "number_literal",
+ doc_comment: "doc_comment",
+ container_doc_comment: "container_doc_comment",
+ line_comment: "line_comment",
+ invalid_periodasterisks: "invalid_periodasterisks",
+ bang: "bang",
+ pipe: "pipe",
+ pipe_pipe: "pipe_pipe",
+ pipe_equal: "pipe_equal",
+ equal: "equal",
+ equal_equal: "equal_equal",
+ equal_angle_bracket_right: "equal_angle_bracket_right",
+ bang_equal: "bang_equal",
+ l_paren: "l_paren",
+ r_paren: "r_paren",
+ semicolon: "semicolon",
+ percent: "percent",
+ percent_equal: "percent_equal",
+ l_brace: "l_brace",
+ r_brace: "r_brace",
+ l_bracket: "l_bracket",
+ r_bracket: "r_bracket",
+ period: "period",
+ period_asterisk: "period_asterisk",
+ ellipsis2: "ellipsis2",
+ ellipsis3: "ellipsis3",
+ caret: "caret",
+ caret_equal: "caret_equal",
+ plus: "plus",
+ plus_plus: "plus_plus",
+ plus_equal: "plus_equal",
+ plus_percent: "plus_percent",
+ plus_percent_equal: "plus_percent_equal",
+ plus_pipe: "plus_pipe",
+ plus_pipe_equal: "plus_pipe_equal",
+ minus: "minus",
+ minus_equal: "minus_equal",
+ minus_percent: "minus_percent",
+ minus_percent_equal: "minus_percent_equal",
+ minus_pipe: "minus_pipe",
+ minus_pipe_equal: "minus_pipe_equal",
+ asterisk: "asterisk",
+ asterisk_equal: "asterisk_equal",
+ asterisk_asterisk: "asterisk_asterisk",
+ asterisk_percent: "asterisk_percent",
+ asterisk_percent_equal: "asterisk_percent_equal",
+ asterisk_pipe: "asterisk_pipe",
+ asterisk_pipe_equal: "asterisk_pipe_equal",
+ arrow: "arrow",
+ colon: "colon",
+ slash: "slash",
+ slash_equal: "slash_equal",
+ comma: "comma",
+ ampersand: "ampersand",
+ ampersand_equal: "ampersand_equal",
+ question_mark: "question_mark",
+ angle_bracket_left: "angle_bracket_left",
+ angle_bracket_left_equal: "angle_bracket_left_equal",
+ angle_bracket_angle_bracket_left: "angle_bracket_angle_bracket_left",
+ angle_bracket_angle_bracket_left_equal: "angle_bracket_angle_bracket_left_equal",
+ angle_bracket_angle_bracket_left_pipe: "angle_bracket_angle_bracket_left_pipe",
+ angle_bracket_angle_bracket_left_pipe_equal: "angle_bracket_angle_bracket_left_pipe_equal",
+ angle_bracket_right: "angle_bracket_right",
+ angle_bracket_right_equal: "angle_bracket_right_equal",
+ angle_bracket_angle_bracket_right: "angle_bracket_angle_bracket_right",
+ angle_bracket_angle_bracket_right_equal: "angle_bracket_angle_bracket_right_equal",
+ tilde: "tilde",
+ keyword_addrspace: "keyword_addrspace",
+ keyword_align: "keyword_align",
+ keyword_allowzero: "keyword_allowzero",
+ keyword_and: "keyword_and",
+ keyword_anyframe: "keyword_anyframe",
+ keyword_anytype: "keyword_anytype",
+ keyword_asm: "keyword_asm",
+ keyword_async: "keyword_async",
+ keyword_await: "keyword_await",
+ keyword_break: "keyword_break",
+ keyword_callconv: "keyword_callconv",
+ keyword_catch: "keyword_catch",
+ keyword_comptime: "keyword_comptime",
+ keyword_const: "keyword_const",
+ keyword_continue: "keyword_continue",
+ keyword_defer: "keyword_defer",
+ keyword_else: "keyword_else",
+ keyword_enum: "keyword_enum",
+ keyword_errdefer: "keyword_errdefer",
+ keyword_error: "keyword_error",
+ keyword_export: "keyword_export",
+ keyword_extern: "keyword_extern",
+ keyword_fn: "keyword_fn",
+ keyword_for: "keyword_for",
+ keyword_if: "keyword_if",
+ keyword_inline: "keyword_inline",
+ keyword_noalias: "keyword_noalias",
+ keyword_noinline: "keyword_noinline",
+ keyword_nosuspend: "keyword_nosuspend",
+ keyword_opaque: "keyword_opaque",
+ keyword_or: "keyword_or",
+ keyword_orelse: "keyword_orelse",
+ keyword_packed: "keyword_packed",
+ keyword_pub: "keyword_pub",
+ keyword_resume: "keyword_resume",
+ keyword_return: "keyword_return",
+ keyword_linksection: "keyword_linksection",
+ keyword_struct: "keyword_struct",
+ keyword_suspend: "keyword_suspend",
+ keyword_switch: "keyword_switch",
+ keyword_test: "keyword_test",
+ keyword_threadlocal: "keyword_threadlocal",
+ keyword_try: "keyword_try",
+ keyword_union: "keyword_union",
+ keyword_unreachable: "keyword_unreachable",
+ keyword_usingnamespace: "keyword_usingnamespace",
+ keyword_var: "keyword_var",
+ keyword_volatile: "keyword_volatile",
+ keyword_while: "keyword_while"
+}
+
+const Tok = {
+ const: { src: "const", tag: Tag.keyword_const },
+ var: { src: "var", tag: Tag.keyword_var },
+ colon: { src: ":", tag: Tag.colon },
+ eql: { src: "=", tag: Tag.equals },
+ space: { src: " ", tag: Tag.whitespace },
+ tab: { src: " ", tag: Tag.whitespace },
+ enter: { src: "\n", tag: Tag.whitespace },
+ semi: { src: ";", tag: Tag.semicolon },
+ l_bracket: { src: "[", tag: Tag.l_bracket },
+ r_bracket: { src: "]", tag: Tag.r_bracket },
+ l_brace: { src: "{", tag: Tag.l_brace },
+ r_brace: { src: "}", tag: Tag.r_brace },
+ l_paren: { src: "(", tag: Tag.l_paren },
+ r_paren: { src: ")", tag: Tag.r_paren },
+ period: { src: ".", tag: Tag.period },
+ comma: { src: ",", tag: Tag.comma },
+ question_mark: { src: "?", tag: Tag.question_mark },
+ asterisk: { src: "*", tag: Tag.asterisk },
+ identifier: (name) => { return { src: name, tag: Tag.identifier } },
+};
+
+
+const State = {
+ start: 0,
+ identifier: 1,
+ builtin: 2,
+ string_literal: 3,
+ string_literal_backslash: 4,
+ multiline_string_literal_line: 5,
+ char_literal: 6,
+ char_literal_backslash: 7,
+ char_literal_hex_escape: 8,
+ char_literal_unicode_escape_saw_u: 9,
+ char_literal_unicode_escape: 10,
+ char_literal_unicode_invalid: 11,
+ char_literal_unicode: 12,
+ char_literal_end: 13,
+ backslash: 14,
+ equal: 15,
+ bang: 16,
+ pipe: 17,
+ minus: 18,
+ minus_percent: 19,
+ minus_pipe: 20,
+ asterisk: 21,
+ asterisk_percent: 22,
+ asterisk_pipe: 23,
+ slash: 24,
+ line_comment_start: 25,
+ line_comment: 26,
+ doc_comment_start: 27,
+ doc_comment: 28,
+ int: 29,
+ int_exponent: 30,
+ int_period: 31,
+ float: 32,
+ float_exponent: 33,
+ ampersand: 34,
+ caret: 35,
+ percent: 36,
+ plus: 37,
+ plus_percent: 38,
+ plus_pipe: 39,
+ angle_bracket_left: 40,
+ angle_bracket_angle_bracket_left: 41,
+ angle_bracket_angle_bracket_left_pipe: 42,
+ angle_bracket_right: 43,
+ angle_bracket_angle_bracket_right: 44,
+ period: 45,
+ period_2: 46,
+ period_asterisk: 47,
+ saw_at_sign: 48,
+ whitespace: 49,
+}
+
+const keywords = {
+ "addrspace": Tag.keyword_addrspace,
+ "align": Tag.keyword_align,
+ "allowzero": Tag.keyword_allowzero,
+ "and": Tag.keyword_and,
+ "anyframe": Tag.keyword_anyframe,
+ "anytype": Tag.keyword_anytype,
+ "asm": Tag.keyword_asm,
+ "async": Tag.keyword_async,
+ "await": Tag.keyword_await,
+ "break": Tag.keyword_break,
+ "callconv": Tag.keyword_callconv,
+ "catch": Tag.keyword_catch,
+ "comptime": Tag.keyword_comptime,
+ "const": Tag.keyword_const,
+ "continue": Tag.keyword_continue,
+ "defer": Tag.keyword_defer,
+ "else": Tag.keyword_else,
+ "enum": Tag.keyword_enum,
+ "errdefer": Tag.keyword_errdefer,
+ "error": Tag.keyword_error,
+ "export": Tag.keyword_export,
+ "extern": Tag.keyword_extern,
+ "fn": Tag.keyword_fn,
+ "for": Tag.keyword_for,
+ "if": Tag.keyword_if,
+ "inline": Tag.keyword_inline,
+ "noalias": Tag.keyword_noalias,
+ "noinline": Tag.keyword_noinline,
+ "nosuspend": Tag.keyword_nosuspend,
+ "opaque": Tag.keyword_opaque,
+ "or": Tag.keyword_or,
+ "orelse": Tag.keyword_orelse,
+ "packed": Tag.keyword_packed,
+ "pub": Tag.keyword_pub,
+ "resume": Tag.keyword_resume,
+ "return": Tag.keyword_return,
+ "linksection": Tag.keyword_linksection,
+ "struct": Tag.keyword_struct,
+ "suspend": Tag.keyword_suspend,
+ "switch": Tag.keyword_switch,
+ "test": Tag.keyword_test,
+ "threadlocal": Tag.keyword_threadlocal,
+ "try": Tag.keyword_try,
+ "union": Tag.keyword_union,
+ "unreachable": Tag.keyword_unreachable,
+ "usingnamespace": Tag.keyword_usingnamespace,
+ "var": Tag.keyword_var,
+ "volatile": Tag.keyword_volatile,
+ "while": Tag.keyword_while,
+};
+
+function make_token(tag, start, end) {
+ return {
+ tag: tag,
+ loc: {
+ start: start,
+ end: end
+ }
+ }
+
+}
+
+function dump_tokens(tokens, raw_source) {
+
+ //TODO: this is not very fast
+ function find_tag_key(tag) {
+ for (const [key, value] of Object.entries(Tag)) {
+ if (value == tag) return key;
+ }
+ }
+
+ for (let i = 0; i < tokens.length; i++) {
+ const tok = tokens[i];
+ const z = raw_source.substring(tok.loc.start, tok.loc.end).toLowerCase();
+ console.log(`${find_tag_key(tok.tag)} "${tok.tag}" '${z}'`)
+ }
+}
+
+function* Tokenizer(raw_source) {
+ let tokenizer = new InnerTokenizer(raw_source);
+ while (true) {
+ let t = tokenizer.next();
+ if (t.tag == Tag.eof)
+ return;
+
+ t.src = raw_source.slice(t.loc.start, t.loc.end);
+
+ yield t;
+ }
+
+}
+function InnerTokenizer(raw_source) {
+ this.index = 0;
+ this.flag = false;
+
+ this.seen_escape_digits = undefined;
+ this.remaining_code_units = undefined;
+
+ this.next = () => {
+ let state = State.start;
+
+ var result = {
+ tag: -1,
+ loc: {
+ start: this.index,
+ end: undefined,
+ },
+ src: undefined,
+ };
+
+ //having a while (true) loop seems like a bad idea the loop should never
+ //take more iterations than twice the length of the source code
+ const MAX_ITERATIONS = raw_source.length * 2;
+ let iterations = 0;
+
+ while (iterations <= MAX_ITERATIONS) {
+
+ if (this.flag) {
+ return make_token(Tag.eof, this.index - 2, this.index - 2);
+ }
+ iterations += 1; // avoid death loops
+
+ var c = raw_source[this.index];
+
+ if (c === undefined) {
+ c = ' '; // push the last token
+ this.flag = true;
+ }
+
+ switch (state) {
+ case State.start:
+ switch (c) {
+ case 0: {
+ if (this.index != raw_source.length) {
+ result.tag = Tag.invalid;
+ result.loc.start = this.index;
+ this.index += 1;
+ result.loc.end = this.index;
+ return result;
+ }
+ result.loc.end = this.index;
+ return result;
+ }
+ case ' ':
+ case '\n':
+ case '\t':
+ case '\r': {
+ state = State.whitespace;
+ result.tag = Tag.whitespace;
+ result.loc.start = this.index;
+ break;
+ }
+ case '"': {
+ state = State.string_literal;
+ result.tag = Tag.string_literal;
+ break;
+ }
+ case '\'': {
+ state = State.char_literal;
+ break;
+ }
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ case '_': {
+ state = State.identifier;
+ result.tag = Tag.identifier;
+ break;
+ }
+ case '@': {
+ state = State.saw_at_sign;
+ break;
+ }
+ case '=': {
+ state = State.equal;
+ break;
+ }
+ case '!': {
+ state = State.bang;
+ break;
+ }
+ case '|': {
+ state = State.pipe;
+ break;
+ }
+ case '(': {
+ result.tag = Tag.l_paren;
+ this.index += 1;
+ result.loc.end = this.index;
+
+ return result;
+
+ }
+ case ')': {
+ result.tag = Tag.r_paren;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+
+ }
+ case '[': {
+ result.tag = Tag.l_bracket;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+
+ }
+ case ']': {
+ result.tag = Tag.r_bracket;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+
+ }
+ case ';': {
+ result.tag = Tag.semicolon;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+
+ }
+ case ',': {
+ result.tag = Tag.comma;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+
+ }
+ case '?': {
+ result.tag = Tag.question_mark;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+
+ }
+ case ':': {
+ result.tag = Tag.colon;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+
+ }
+ case '%': {
+ state = State.percent; break;
+ }
+ case '*': {
+ state = State.asterisk; break;
+ }
+ case '+': {
+ state = State.plus; break;
+ }
+ case '<': {
+ state = State.angle_bracket_left; break;
+ }
+ case '>': {
+ state = State.angle_bracket_right; break;
+ }
+ case '^': {
+ state = State.caret; break;
+ }
+ case '\\': {
+ state = State.backslash;
+ result.tag = Tag.multiline_string_literal_line; break;
+ }
+ case '{': {
+ result.tag = Tag.l_brace;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+
+ }
+ case '}': {
+ result.tag = Tag.r_brace;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+
+ }
+ case '~': {
+ result.tag = Tag.tilde;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+
+ }
+ case '.': {
+ state = State.period; break;
+ }
+ case '-': {
+ state = State.minus; break;
+ }
+ case '/': {
+ state = State.slash; break;
+ }
+ case '&': {
+ state = State.ampersand; break;
+ }
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ state = State.int;
+ result.tag = Tag.number_literal; break;
+ }
+ default: {
+ result.tag = Tag.invalid;
+ result.loc.end = this.index;
+ this.index += 1;
+ return result;
+ }
+ }
+ break;
+ case State.saw_at_sign:
+ switch (c) {
+ case '"': {
+ result.tag = Tag.identifier;
+ state = State.string_literal; break;
+ }
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ case '_': {
+ state = State.builtin;
+ result.tag = Tag.builtin;
+ break;
+ }
+ default: {
+ result.tag = Tag.invalid;
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.ampersand:
+ switch (c) {
+ case '=': {
+ result.tag = Tag.ampersand_equal;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ result.tag = Tag.ampersand; result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.asterisk: switch (c) {
+ case '=': {
+ result.tag = Tag.asterisk_equal;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+ }
+ case '*': {
+ result.tag = Tag.asterisk_asterisk;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+ }
+ case '%': {
+ state = State.asterisk_percent; break;
+ }
+ case '|': {
+ state = State.asterisk_pipe; break;
+ }
+ default: {
+ result.tag = Tag.asterisk;
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.asterisk_percent:
+ switch (c) {
+ case '=': {
+ result.tag = Tag.asterisk_percent_equal;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ result.tag = Tag.asterisk_percent;
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.asterisk_pipe:
+ switch (c) {
+ case '=': {
+ result.tag = Tag.asterisk_pipe_equal;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ result.tag = Tag.asterisk_pipe; result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.percent:
+ switch (c) {
+ case '=': {
+ result.tag = Tag.percent_equal;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ result.tag = Tag.percent; result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.plus:
+ switch (c) {
+ case '=': {
+ result.tag = Tag.plus_equal;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+ }
+ case '+': {
+ result.tag = Tag.plus_plus;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+ }
+ case '%': {
+ state = State.plus_percent; break;
+ }
+ case '|': {
+ state = State.plus_pipe; break;
+ }
+ default: {
+ result.tag = Tag.plus; result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.plus_percent:
+ switch (c) {
+ case '=': {
+ result.tag = Tag.plus_percent_equal;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ result.tag = Tag.plus_percent; result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.plus_pipe:
+ switch (c) {
+ case '=': {
+ result.tag = Tag.plus_pipe_equal;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ result.tag = Tag.plus_pipe; result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.caret:
+ switch (c) {
+ case '=': {
+ result.tag = Tag.caret_equal;
+ this.index += 1; result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ result.tag = Tag.caret; result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.identifier:
+ switch (c) {
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ case '_':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': break;
+ default: {
+ // if (Token.getKeyword(buffer[result.loc.start..this.index])) | tag | {
+ const z = raw_source.substring(result.loc.start, this.index);
+ if (z in keywords) {
+ result.tag = keywords[z];
+ }
+ result.loc.end = this.index;
+ return result;
+ }
+
+
+ }
+ break;
+ case State.builtin: switch (c) {
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ case '_':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': break;
+ default: result.loc.end = this.index;
+ return result;
+ }
+ break;
+ case State.backslash:
+ switch (c) {
+ case '\\': {
+ state = State.multiline_string_literal_line;
+ break;
+ }
+ default: {
+ result.tag = Tag.invalid;
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.string_literal:
+ switch (c) {
+ case '\\': {
+ state = State.string_literal_backslash; break;
+ }
+ case '"': {
+ this.index += 1;
+ result.loc.end = this.index;
+
+ return result;
+ }
+ case 0: {
+ //TODO: PORT
+ // if (this.index == buffer.len) {
+ // result.tag = .invalid;
+ // break;
+ // } else {
+ // checkLiteralCharacter();
+ // }
+ result.loc.end = this.index;
+ return result;
+ }
+ case '\n': {
+ result.tag = Tag.invalid;
+ result.loc.end = this.index;
+ return result;
+ }
+ //TODO: PORT
+ //default: checkLiteralCharacter(),
+ }
+ break;
+ case State.string_literal_backslash:
+ switch (c) {
+ case 0:
+ case '\n': {
+ result.tag = Tag.invalid;
+ result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ state = State.string_literal; break;
+ }
+ }
+ break;
+ case State.char_literal: switch (c) {
+ case 0: {
+ result.tag = Tag.invalid;
+ result.loc.end = this.index;
+ return result;
+ }
+ case '\\': {
+ state = State.char_literal_backslash;
+ break;
+ }
+ //TODO: PORT
+ // '\'', 0x80...0xbf, 0xf8...0xff => {
+ // result.tag = .invalid;
+ // break;
+ // },
+ // 0xc0...0xdf => { // 110xxxxx
+ // this.remaining_code_units = 1;
+ // state = .char_literal_unicode;
+ // },
+ // 0xe0...0xef => { // 1110xxxx
+ // this.remaining_code_units = 2;
+ // state = .char_literal_unicode;
+ // },
+ // 0xf0...0xf7 => { // 11110xxx
+ // this.remaining_code_units = 3;
+ // state = .char_literal_unicode;
+ // },
+
+ // case 0x80:
+ // case 0x81:
+ // case 0x82:
+ // case 0x83:
+ // case 0x84:
+ // case 0x85:
+ // case 0x86:
+ // case 0x87:
+ // case 0x88:
+ // case 0x89:
+ // case 0x8a:
+ // case 0x8b:
+ // case 0x8c:
+ // case 0x8d:
+ // case 0x8e:
+ // case 0x8f:
+ // case 0x90:
+ // case 0x91:
+ // case 0x92:
+ // case 0x93:
+ // case 0x94:
+ // case 0x95:
+ // case 0x96:
+ // case 0x97:
+ // case 0x98:
+ // case 0x99:
+ // case 0x9a:
+ // case 0x9b:
+ // case 0x9c:
+ // case 0x9d:
+ // case 0x9e:
+ // case 0x9f:
+ // case 0xa0:
+ // case 0xa1:
+ // case 0xa2:
+ // case 0xa3:
+ // case 0xa4:
+ // case 0xa5:
+ // case 0xa6:
+ // case 0xa7:
+ // case 0xa8:
+ // case 0xa9:
+ // case 0xaa:
+ // case 0xab:
+ // case 0xac:
+ // case 0xad:
+ // case 0xae:
+ // case 0xaf:
+ // case 0xb0:
+ // case 0xb1:
+ // case 0xb2:
+ // case 0xb3:
+ // case 0xb4:
+ // case 0xb5:
+ // case 0xb6:
+ // case 0xb7:
+ // case 0xb8:
+ // case 0xb9:
+ // case 0xba:
+ // case 0xbb:
+ // case 0xbc:
+ // case 0xbd:
+ // case 0xbe:
+ // case 0xbf:
+ // case 0xf8:
+ // case 0xf9:
+ // case 0xfa:
+ // case 0xfb:
+ // case 0xfc:
+ // case 0xfd:
+ // case 0xfe:
+ // case 0xff:
+ // result.tag = .invalid;
+ // break;
+ // case 0xc0:
+ // case 0xc1:
+ // case 0xc2:
+ // case 0xc3:
+ // case 0xc4:
+ // case 0xc5:
+ // case 0xc6:
+ // case 0xc7:
+ // case 0xc8:
+ // case 0xc9:
+ // case 0xca:
+ // case 0xcb:
+ // case 0xcc:
+ // case 0xcd:
+ // case 0xce:
+ // case 0xcf:
+ // case 0xd0:
+ // case 0xd1:
+ // case 0xd2:
+ // case 0xd3:
+ // case 0xd4:
+ // case 0xd5:
+ // case 0xd6:
+ // case 0xd7:
+ // case 0xd8:
+ // case 0xd9:
+ // case 0xda:
+ // case 0xdb:
+ // case 0xdc:
+ // case 0xdd:
+ // case 0xde:
+ // case 0xdf:
+ // this.remaining_code_units = 1;
+ // state = .char_literal_unicode;
+ // case 0xe0:
+ // case 0xe1:
+ // case 0xe2:
+ // case 0xe3:
+ // case 0xe4:
+ // case 0xe5:
+ // case 0xe6:
+ // case 0xe7:
+ // case 0xe8:
+ // case 0xe9:
+ // case 0xea:
+ // case 0xeb:
+ // case 0xec:
+ // case 0xed:
+ // case 0xee:
+ // case 0xef:
+ // this.remaining_code_units = 2;
+ // state = .char_literal_unicode;
+ // case 0xf0:
+ // case 0xf1:
+ // case 0xf2:
+ // case 0xf3:
+ // case 0xf4:
+ // case 0xf5:
+ // case 0xf6:
+ // case 0xf7:
+ // this.remaining_code_units = 3;
+ // state = .char_literal_unicode;
+
+ case '\n': {
+ result.tag = Tag.invalid;
+ result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ state = State.char_literal_end; break;
+ }
+ }
+ break;
+ case State.char_literal_backslash:
+ switch (c) {
+ case 0:
+ case '\n': {
+ result.tag = Tag.invalid;
+ result.loc.end = this.index;
+ return result;
+ }
+ case 'x': {
+ state = State.char_literal_hex_escape;
+ this.seen_escape_digits = 0; break;
+ }
+ case 'u': {
+ state = State.char_literal_unicode_escape_saw_u; break;
+ }
+ default: {
+ state = State.char_literal_end; break;
+ }
+ }
+ break;
+ case State.char_literal_hex_escape:
+ switch (c) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F': {
+ this.seen_escape_digits += 1;
+ if (this.seen_escape_digits == 2) {
+ state = State.char_literal_end;
+ } break;
+ }
+ default: {
+ result.tag = Tag.invalid;
+ esult.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.char_literal_unicode_escape_saw_u:
+ switch (c) {
+ case 0: {
+ result.tag = Tag.invalid;
+ result.loc.end = this.index;
+ return result;
+ }
+ case '{': {
+ state = State.char_literal_unicode_escape; break;
+ }
+ default: {
+ result.tag = Tag.invalid;
+ state = State.char_literal_unicode_invalid; break;
+ }
+ }
+ break;
+ case State.char_literal_unicode_escape:
+ switch (c) {
+ case 0: {
+ result.tag = Tag.invalid;
+ result.loc.end = this.index;
+ return result;
+ }
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F': break;
+ case '}': {
+ state = State.char_literal_end; // too many/few digits handled later
+ break;
+ }
+ default: {
+ result.tag = Tag.invalid;
+ state = State.char_literal_unicode_invalid; break;
+ }
+ }
+ break;
+ case State.char_literal_unicode_invalid:
+ switch (c) {
+ // Keep consuming characters until an obvious stopping point.
+ // This consolidates e.g. `u{0ab1Q}` into a single invalid token
+ // instead of creating the tokens `u{0ab1`, `Q`, `}`
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ case '}':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': break;
+ default: break;
+ }
+ break;
+ case State.char_literal_end:
+ switch (c) {
+ case '\'': {
+ result.tag = Tag.char_literal;
+ this.index += 1;
+ result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ result.tag = Tag.invalid;
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.char_literal_unicode:
+ switch (c) {
+ // 0x80...0xbf => {
+ // this.remaining_code_units -= 1;
+ // if (this.remaining_code_units == 0) {
+ // state = .char_literal_end;
+ // }
+ // },
+ default: {
+ result.tag = Tag.invalid;
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.multiline_string_literal_line:
+ switch (c) {
+ case 0:
+ result.loc.end = this.index;
+ return result;
+ case '\n': {
+
+ this.index += 1;
+ result.loc.end = this.index;
+ return result;
+ }
+ case '\t': break;
+ //TODO: PORT
+ //default: checkLiteralCharacter(),
+
+ }
+ break;
+ case State.bang:
+ switch (c) {
+ case '=': {
+ result.tag = Tag.bang_equal;
+ this.index += 1;
+ result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ result.tag = Tag.bang;
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.pipe:
+ switch (c) {
+ case '=': {
+ result.tag = Tag.pipe_equal;
+ this.index += 1;
+ result.loc.end = this.index;
+ return result;
+ }
+ case '|': {
+ result.tag = Tag.pipe_pipe;
+ this.index += 1;
+ result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ result.tag = Tag.pipe;
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.equal: switch (c) {
+ case '=': {
+ result.tag = Tag.equal_equal;
+ this.index += 1;
+ result.loc.end = this.index;
+ return result;
+ }
+ case '>': {
+ result.tag = Tag.equal_angle_bracket_right;
+ this.index += 1;
+ result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ result.tag = Tag.equal;
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.minus: switch (c) {
+ case '>': {
+ result.tag = Tag.arrow;
+ this.index += 1;
+ result.loc.end = this.index;
+ return result;
+ }
+ case '=': {
+ result.tag = Tag.minus_equal;
+ this.index += 1;
+ result.loc.end = this.index;
+ return result;
+ }
+ case '%': {
+ state = State.minus_percent; break;
+ }
+ case '|': {
+ state = State.minus_pipe; break;
+ }
+ default: {
+ result.tag = Tag.minus;
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.minus_percent:
+ switch (c) {
+ case '=': {
+ result.tag = Tag.minus_percent_equal;
+ this.index += 1;
+ result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ result.tag = Tag.minus_percent;
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.minus_pipe:
+ switch (c) {
+ case '=': {
+ result.tag = Tag.minus_pipe_equal;
+ this.index += 1;
+ result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ result.tag = Tag.minus_pipe;
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.angle_bracket_left:
+ switch (c) {
+ case '<': {
+ state = State.angle_bracket_angle_bracket_left; break;
+ }
+ case '=': {
+ result.tag = Tag.angle_bracket_left_equal;
+ this.index += 1;
+ result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ result.tag = Tag.angle_bracket_left;
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.angle_bracket_angle_bracket_left:
+ switch (c) {
+ case '=': {
+ result.tag = Tag.angle_bracket_angle_bracket_left_equal;
+ this.index += 1;
+ result.loc.end = this.index;
+ return result;
+ }
+ case '|': {
+ state = State.angle_bracket_angle_bracket_left_pipe;
+ }
+ default: {
+ result.tag = Tag.angle_bracket_angle_bracket_left;
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.angle_bracket_angle_bracket_left_pipe:
+ switch (c) {
+ case '=': {
+ result.tag = Tag.angle_bracket_angle_bracket_left_pipe_equal;
+ this.index += 1;
+ result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ result.tag = Tag.angle_bracket_angle_bracket_left_pipe;
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.angle_bracket_right:
+ switch (c) {
+ case '>': {
+ state = State.angle_bracket_angle_bracket_right; break;
+ }
+ case '=': {
+ result.tag = Tag.angle_bracket_right_equal;
+ this.index += 1;
+ result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ result.tag = Tag.angle_bracket_right;
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.angle_bracket_angle_bracket_right:
+ switch (c) {
+ case '=': {
+ result.tag = Tag.angle_bracket_angle_bracket_right_equal;
+ this.index += 1;
+ result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ result.tag = Tag.angle_bracket_angle_bracket_right;
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.period:
+ switch (c) {
+ case '.': {
+ state = State.period_2; break;
+ }
+ case '*': {
+ state = State.period_asterisk; break;
+ }
+ default: {
+ result.tag = Tag.period;
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.period_2:
+ switch (c) {
+ case '.': {
+ result.tag = Tag.ellipsis3;
+ this.index += 1;
+ result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ result.tag = Tag.ellipsis2;
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.period_asterisk:
+ switch (c) {
+ case '*': {
+ result.tag = Tag.invalid_periodasterisks;
+ result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ result.tag = Tag.period_asterisk;
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ break;
+ case State.slash:
+ switch (c) {
+ case '/': {
+ state = State.line_comment_start;
+ break;
+ }
+ case '=': {
+ result.tag = Tag.slash_equal;
+ this.index += 1;
+ result.loc.end = this.index;
+ return result;
+ }
+ default: {
+ result.tag = Tag.slash;
+ result.loc.end = this.index;
+ return result;
+ }
+ } break;
+ case State.line_comment_start:
+ switch (c) {
+ case 0: {
+ if (this.index != raw_source.length) {
+ result.tag = Tag.invalid;
+ this.index += 1;
+ }
+ result.loc.end = this.index;
+ return result;
+ }
+ case '/': {
+ state = State.doc_comment_start; break;
+ }
+ case '!': {
+ result.tag = Tag.container_doc_comment;
+ state = State.doc_comment; break;
+ }
+ case '\n': {
+ state = State.start;
+ result.loc.start = this.index + 1; break;
+ }
+ case '\t':
+ state = State.line_comment; break;
+ default: {
+ state = State.line_comment;
+ //TODO: PORT
+ //checkLiteralCharacter();
+ break;
+ }
+ } break;
+ case State.doc_comment_start:
+ switch (c) {
+ case '/': {
+ state = State.line_comment; break;
+ }
+ case 0:
+ case '\n':
+ {
+ result.tag = Tag.doc_comment;
+ result.loc.end = this.index;
+ return result;
+ }
+ case '\t': {
+ state = State.doc_comment;
+ result.tag = Tag.doc_comment; break;
+ }
+ default: {
+ state = State.doc_comment;
+ result.tag = Tag.doc_comment;
+ //TODO: PORT
+ //checkLiteralCharacter();
+ break;
+ }
+ } break;
+ case State.line_comment:
+ switch (c) {
+ case 0: {
+ if (this.index != raw_source.length) {
+ result.tag = Tag.invalid;
+ this.index += 1;
+ }
+ result.loc.end = this.index;
+ return result;
+ }
+ case '\n': {
+ result.tag = Tag.line_comment;
+ result.loc.end = this.index;
+ return result;
+ }
+ case '\t': break;
+ //TODO: PORT
+ //default: checkLiteralCharacter(),
+ } break;
+ case State.doc_comment:
+ switch (c) {
+ case 0://
+ case '\n':
+ result.loc.end = this.index;
+ return result;
+ case '\t': break;
+ //TODOL PORT
+ // default: checkLiteralCharacter(),
+ default:
+ break;
+ } break;
+ case State.int:
+ switch (c) {
+ case '.':
+ state = State.int_period;
+ break;
+ case '_':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ break;
+ case 'e':
+ case 'E':
+ case 'p':
+ case 'P':
+ state = State.int_exponent;
+ break;
+ default: result.loc.end = this.index;
+ return result;
+ } break;
+ case State.int_exponent:
+ switch (c) {
+ case '-':
+ case '+':
+ {
+ ``
+ state = State.float; break;
+ }
+ default: {
+ this.index -= 1;
+ state = State.int; break;
+ }
+ } break;
+ case State.int_period: switch (c) {
+ case '_':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': {
+ state = State.float; break;
+ }
+ case 'e':
+ case 'E':
+ case 'p':
+ case 'P':
+ state = State.float_exponent; break;
+ default: {
+ this.index -= 1;
+ result.loc.end = this.index;
+ return result;
+ }
+ } break;
+ case State.float:
+ switch (c) {
+ case '_':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ break;
+
+ case 'e':
+ case 'E':
+ case 'p':
+ case 'P':
+ state = State.float_exponent; break;
+ default: result.loc.end = this.index;
+ return result;
+ } break;
+ case State.float_exponent:
+ switch (c) {
+ case '-':
+ case '+':
+ state = State.float; break;
+ default: {
+ this.index -= 1;
+ state = State.float; break;
+ }
+ }
+ break;
+
+ case State.whitespace:
+ switch(c) {
+ case ' ':
+ case '\n':
+ case '\t':
+ case '\r': {
+ break;
+ }
+ default: {
+ result.loc.end = this.index;
+ return result;
+ }
+ }
+ }
+ this.index += 1;
+ }
+
+ //TODO: PORT
+ // if (result.tag == Tag.eof) {
+ // if (pending_invalid_token) | token | {
+ // pending_invalid_token = null;
+ // return token;
+ // }
+ // result.loc.start = sindex;
+ // }
+
+ result.loc.end = this.index;
+ return result;
+
+ }
+}
+
+
+const builtin_types = [
+ "f16", "f32", "f64", "f80", "f128",
+ "c_longdouble", "c_short", "c_ushort", "c_int", "c_uint",
+ "c_long", "c_ulong", "c_longlong", "c_ulonglong", "c_char",
+ "anyopaque", "void", "bool", "isize", "usize",
+ "noreturn", "type", "anyerror", "comptime_int", "comptime_float",
+];
+
+function isSimpleType(typeName) {
+ return builtin_types.includes(typeName) || isIntType(typeName);
+}
+
+function isIntType(typeName) {
+ if (typeName[0] != 'u' && typeName[0] != 'i') return false;
+ let i = 1;
+ if (i == typeName.length) return false;
+ for (; i < typeName.length; i += 1) {
+ if (typeName[i] < '0' || typeName[i] > '9') return false;
+ }
+ return true;
+}
+
+function isSpecialIndentifier(identifier) {
+ return ["null", "true", "false", ,"undefined"].includes(identifier);
+}
+
+//const fs = require('fs');
+//const src = fs.readFileSync("../std/c.zig", 'utf8');
+//console.log(generate_html_for_src(src));
+
+
+// gist for zig_lexer_test code: https://gist.github.com/Myvar/2684ba4fb86b975274629d6f21eddc7b
+// // Just for testing not to commit in pr
+// var isNode = new Function("try {return this===global;}catch(e){return false;}");
+// if (isNode()) {
+
+
+// //const s = "const std = @import(\"std\");";
+// //const toksa = tokenize_zig_source(s);
+// //dump_tokens(toksa, s);
+// //console.log(JSON.stringify(toksa));
+
+// const fs = require('fs');
+
+// function testFile(fileName) {
+// //console.log(fileName);
+// var exec = require('child_process').execFileSync;
+// var passed = true;
+// const zig_data = exec('./zig_lexer_test', [fileName]);
+// const data = fs.readFileSync(fileName, 'utf8');
+
+// const toks = tokenize_zig_source(data);
+// const a_json = toks;
+
+// // dump_tokens(a_json, data);
+// // return;
+
+// const b_json = JSON.parse(zig_data.toString());
+
+// if (a_json.length !== b_json.length) {
+// console.log("FAILED a and be is not the same length");
+// passed = false;
+// //return;
+// }
+
+// let len = a_json.length;
+// if (len >= b_json.length) len = b_json.length;
+
+// for (let i = 0; i < len; i++) {
+// const a = a_json[i];
+// const b = b_json[i];
+
+// // console.log(a.tag + " == " + b.tag);
+
+// if (a.tag !== b.tag) {
+
+// // console.log("Around here:");
+// // console.log(
+// // data.substring(b_json[i - 2].loc.start, b_json[i - 2].loc.end),
+// // data.substring(b_json[i - 1].loc.start, b_json[i - 1].loc.end),
+// // data.substring(b_json[i].loc.start, b_json[i].loc.end),
+// // data.substring(b_json[i + 1].loc.start, b_json[i + 1].loc.end),
+// // data.substring(b_json[i + 2].loc.start, b_json[i + 2].loc.end),
+// // );
+
+// console.log("TAG: a != b");
+// console.log("js", a.tag);
+// console.log("zig", b.tag);
+// passed = false;
+// return;
+// }
+
+// if (a.tag !== Tag.eof && a.loc.start !== b.loc.start) {
+// console.log("START: a != b");
+
+// console.log("js", "\"" + data.substring(a_json[i ].loc.start, a_json[i].loc.end) + "\"");
+// console.log("zig", "\"" + data.substring(b_json[i ].loc.start, b_json[i].loc.end) + "\"");
+
+
+// passed = false;
+// return;
+// }
+
+// // if (a.tag !== Tag.eof && a.loc.end !== b.loc.end) {
+// // console.log("END: a != b");
+// // // console.log("Around here:");
+// // // console.log(
+// // // // data.substring(b_json[i - 2].loc.start, b_json[i - 2].loc.end),
+// // // // data.substring(b_json[i - 1].loc.start, b_json[i - 1].loc.end),
+// // // data.substring(b_json[i ].loc.start, b_json[i].loc.end),
+// // // // data.substring(b_json[i + 1].loc.start, b_json[i + 1].loc.end),
+// // // // data.substring(b_json[i + 2].loc.start, b_json[i + 2].loc.end),
+// // // );
+// // console.log("js", "\"" + data.substring(a_json[i ].loc.start, a_json[i].loc.end) + "\"");
+// // console.log("zig", "\"" + data.substring(b_json[i ].loc.start, b_json[i].loc.end) + "\"");
+// // passed = false;
+// // return;
+// // }
+// }
+// return passed;
+// }
+// var path = require('path');
+// function fromDir(startPath, filter) {
+// if (!fs.existsSync(startPath)) {
+// console.log("no dir ", startPath);
+// return;
+// }
+// var files = fs.readdirSync(startPath);
+// for (var i = 0; i < files.length; i++) {
+// var filename = path.join(startPath, files[i]);
+// var stat = fs.lstatSync(filename);
+// if (stat.isDirectory()) {
+// fromDir(filename, filter); //recurse
+// } else if (filename.endsWith(filter)) {
+// try {
+// console.log('-- TESTING: ', filename);
+// console.log("\t\t", testFile(filename));
+// }
+// catch {
+// }
+// };
+// };
+// };
+// fromDir('../std', '.zig');
+// //console.log(testFile("/home/myvar/code/zig/lib/std/fmt/errol.zig"));
+// //console.log(testFile("test.zig"));
+// }
\ No newline at end of file