[{"data":1,"prerenderedAt":5770},["ShallowReactive",2],{"page-\u002Flua\u002F10-io-and-files":3},{"id":4,"title":5,"body":6,"description":5761,"extension":5762,"meta":5763,"navigation":176,"path":5764,"seo":5765,"stem":5768,"__hash__":5769},"content\u002Flua\u002F10-io-and-files.md","Lua 10 — I\u002FO, Files, Streams & Binary Protocols",{"type":7,"value":8,"toc":5750},"minimark",[9,14,59,66,919,923,5746],[10,11,13],"h1",{"id":12},"_10-io-files-streams-binary-protocols","10 — I\u002FO, Files, Streams & Binary Protocols",[15,16,17,18,22,23,26,27,30,31,34,35,39,40,43,44,26,47,50,51,54,55,58],"p",{},"Lua's I\u002FO is minimal: ",[19,20,21],"code",{},"io.open"," returns a file handle (userdata with methods), ",[19,24,25],{},"io.read","\u002F",[19,28,29],{},"io.write"," for stdin\u002Fstdout, ",[19,32,33],{},"io.popen"," for subprocesses. File handles are ",[36,37,38],"strong",{},"not"," garbage-collected promptly — you must close them explicitly or use ",[19,41,42],{},"\u003Cclose>"," (5.4+). Binary I\u002FO uses strings as byte buffers with ",[19,45,46],{},"string.byte",[19,48,49],{},"string.char",". There is no directory listing in the standard library — use ",[19,52,53],{},"lfs"," (LuaFileSystem) or ",[19,56,57],{},"io.popen(\"ls\")",".",[60,61,63,64],"h2",{"id":62},"file-handles-ioopen","File Handles & ",[19,65,21],{},[67,68,70,285,289],"code-wrapper",{"language":69},"lua",[71,72,76],"pre",{"className":73,"code":74,"language":69,"meta":75,"style":75},"language-lua shiki shiki-themes github-light github-dark","-- io.open(path, mode): returns file handle (userdata) or nil + error message\nlocal f, err = io.open(\"data.txt\", \"r\")\nif not f then\n  error(\"cannot open: \" .. (err or \"unknown\"))  -- err: \"data.txt: No such file or directory\"\nend\n\n-- Modes:\n-- \"r\"  read (file must exist)\n-- \"w\"  write (truncate\u002Fcreate)\n-- \"a\"  append (create if not exists, write at end)\n-- \"r+\" read\u002Fwrite (file must exist, no truncate)\n-- \"w+\" read\u002Fwrite (truncate\u002Fcreate)\n-- \"a+\" read\u002Fappend (create, write at end, read from anywhere)\n-- \"b\"  binary mode (append to any mode: \"rb\", \"wb\", \"r+b\") — no line-ending translation\n-- \"r\"  text mode: on Windows, \\r\\n → \\n on read, \\n → \\r\\n on write\n-- \"rb\" binary mode: exact bytes, no translation (use for binary formats)\n\n-- File handle methods (called with : syntax):\n-- f:read(format)     → read (see below)\n-- f:write(...)       → write strings (variadic)\n-- f:lines()          → iterator over lines\n-- f:seek(whence, offset) → set\u002Fget position\n-- f:flush()          → flush buffer to OS\n-- f:close()          → close (MUST call — not reliably GC'd)\n","",[19,77,78,87,121,136,165,171,178,184,190,196,202,208,214,220,226,232,238,243,249,255,261,267,273,279],{"__ignoreMap":75},[79,80,83],"span",{"class":81,"line":82},"line",1,[79,84,86],{"class":85},"sdCPZ","-- io.open(path, mode): returns file handle (userdata) or nil + error message\n",[79,88,90,94,98,101,105,108,112,115,118],{"class":81,"line":89},2,[79,91,93],{"class":92},"svdQ7","local",[79,95,97],{"class":96},"ssxIu"," f, err ",[79,99,100],{"class":92},"=",[79,102,104],{"class":103},"snvgF"," io.open",[79,106,107],{"class":96},"(",[79,109,111],{"class":110},"sJ6F3","\"data.txt\"",[79,113,114],{"class":96},", ",[79,116,117],{"class":110},"\"r\"",[79,119,120],{"class":96},")\n",[79,122,124,127,130,133],{"class":81,"line":123},3,[79,125,126],{"class":92},"if",[79,128,129],{"class":92}," not",[79,131,132],{"class":96}," f ",[79,134,135],{"class":92},"then\n",[79,137,139,142,144,147,150,153,156,159,162],{"class":81,"line":138},4,[79,140,141],{"class":103},"  error",[79,143,107],{"class":96},[79,145,146],{"class":110},"\"cannot open: \" ",[79,148,149],{"class":92},"..",[79,151,152],{"class":96}," (err ",[79,154,155],{"class":92},"or",[79,157,158],{"class":110}," \"unknown\"",[79,160,161],{"class":96},"))  ",[79,163,164],{"class":85},"-- err: \"data.txt: No such file or directory\"\n",[79,166,168],{"class":81,"line":167},5,[79,169,170],{"class":92},"end\n",[79,172,174],{"class":81,"line":173},6,[79,175,177],{"emptyLinePlaceholder":176},true,"\n",[79,179,181],{"class":81,"line":180},7,[79,182,183],{"class":85},"-- Modes:\n",[79,185,187],{"class":81,"line":186},8,[79,188,189],{"class":85},"-- \"r\"  read (file must exist)\n",[79,191,193],{"class":81,"line":192},9,[79,194,195],{"class":85},"-- \"w\"  write (truncate\u002Fcreate)\n",[79,197,199],{"class":81,"line":198},10,[79,200,201],{"class":85},"-- \"a\"  append (create if not exists, write at end)\n",[79,203,205],{"class":81,"line":204},11,[79,206,207],{"class":85},"-- \"r+\" read\u002Fwrite (file must exist, no truncate)\n",[79,209,211],{"class":81,"line":210},12,[79,212,213],{"class":85},"-- \"w+\" read\u002Fwrite (truncate\u002Fcreate)\n",[79,215,217],{"class":81,"line":216},13,[79,218,219],{"class":85},"-- \"a+\" read\u002Fappend (create, write at end, read from anywhere)\n",[79,221,223],{"class":81,"line":222},14,[79,224,225],{"class":85},"-- \"b\"  binary mode (append to any mode: \"rb\", \"wb\", \"r+b\") — no line-ending translation\n",[79,227,229],{"class":81,"line":228},15,[79,230,231],{"class":85},"-- \"r\"  text mode: on Windows, \\r\\n → \\n on read, \\n → \\r\\n on write\n",[79,233,235],{"class":81,"line":234},16,[79,236,237],{"class":85},"-- \"rb\" binary mode: exact bytes, no translation (use for binary formats)\n",[79,239,241],{"class":81,"line":240},17,[79,242,177],{"emptyLinePlaceholder":176},[79,244,246],{"class":81,"line":245},18,[79,247,248],{"class":85},"-- File handle methods (called with : syntax):\n",[79,250,252],{"class":81,"line":251},19,[79,253,254],{"class":85},"-- f:read(format)     → read (see below)\n",[79,256,258],{"class":81,"line":257},20,[79,259,260],{"class":85},"-- f:write(...)       → write strings (variadic)\n",[79,262,264],{"class":81,"line":263},21,[79,265,266],{"class":85},"-- f:lines()          → iterator over lines\n",[79,268,270],{"class":81,"line":269},22,[79,271,272],{"class":85},"-- f:seek(whence, offset) → set\u002Fget position\n",[79,274,276],{"class":81,"line":275},23,[79,277,278],{"class":85},"-- f:flush()          → flush buffer to OS\n",[79,280,282],{"class":81,"line":281},24,[79,283,284],{"class":85},"-- f:close()          → close (MUST call — not reliably GC'd)\n",[60,286,288],{"id":287},"reading","Reading",[67,290,291,622,627],{"language":69},[71,292,294],{"className":73,"code":293,"language":69,"meta":75,"style":75},"local f = assert(io.open(\"data.txt\", \"r\"))\n\n-- Read formats:\n-- \"*a\" or \"a\"  → entire file (all remaining content)\n-- \"*l\" or \"l\"  → next line (without newline) — DEFAULT in 5.3+\n-- \"*L\" or \"L\"  → next line (WITH newline) — Lua 5.3+\n-- \"*n\" or \"n\"  → next number (reads a number, skips whitespace)\n-- number       → that many bytes (or fewer at EOF)\n\nlocal all = f:read(\"*a\")     -- entire file as one string\nf:seek(\"set\")               -- rewind to start\nlocal line1 = f:read(\"*l\")  -- first line (no \\n)\nf:seek(\"set\")\nlocal line1_with_nl = f:read(\"*L\")  -- first line (with \\n)\nf:seek(\"set\")\nlocal num = f:read(\"*n\")     -- reads a leading number from file\nf:seek(\"set\")\nlocal chunk = f:read(100)    -- first 100 bytes\n\n-- Multiple reads in one call (each arg is a format):\nf:seek(\"set\")\nlocal l1, l2, l3 = f:read(\"*l\", \"*l\", \"*l\")  -- first 3 lines\nf:close()\n",[19,295,296,322,326,331,336,341,346,351,356,360,390,411,437,451,476,490,515,529,555,559,564,578,610],{"__ignoreMap":75},[79,297,298,300,302,304,307,309,311,313,315,317,319],{"class":81,"line":82},[79,299,93],{"class":92},[79,301,132],{"class":96},[79,303,100],{"class":92},[79,305,306],{"class":103}," assert",[79,308,107],{"class":96},[79,310,21],{"class":103},[79,312,107],{"class":96},[79,314,111],{"class":110},[79,316,114],{"class":96},[79,318,117],{"class":110},[79,320,321],{"class":96},"))\n",[79,323,324],{"class":81,"line":89},[79,325,177],{"emptyLinePlaceholder":176},[79,327,328],{"class":81,"line":123},[79,329,330],{"class":85},"-- Read formats:\n",[79,332,333],{"class":81,"line":138},[79,334,335],{"class":85},"-- \"*a\" or \"a\"  → entire file (all remaining content)\n",[79,337,338],{"class":81,"line":167},[79,339,340],{"class":85},"-- \"*l\" or \"l\"  → next line (without newline) — DEFAULT in 5.3+\n",[79,342,343],{"class":81,"line":173},[79,344,345],{"class":85},"-- \"*L\" or \"L\"  → next line (WITH newline) — Lua 5.3+\n",[79,347,348],{"class":81,"line":180},[79,349,350],{"class":85},"-- \"*n\" or \"n\"  → next number (reads a number, skips whitespace)\n",[79,352,353],{"class":81,"line":186},[79,354,355],{"class":85},"-- number       → that many bytes (or fewer at EOF)\n",[79,357,358],{"class":81,"line":192},[79,359,177],{"emptyLinePlaceholder":176},[79,361,362,364,367,369,373,376,379,381,384,387],{"class":81,"line":198},[79,363,93],{"class":92},[79,365,366],{"class":96}," all ",[79,368,100],{"class":92},[79,370,372],{"class":371},"sIsaT"," f",[79,374,375],{"class":96},":",[79,377,378],{"class":103},"read",[79,380,107],{"class":96},[79,382,383],{"class":110},"\"*a\"",[79,385,386],{"class":96},")     ",[79,388,389],{"class":85},"-- entire file as one string\n",[79,391,392,395,397,400,402,405,408],{"class":81,"line":204},[79,393,394],{"class":371},"f",[79,396,375],{"class":96},[79,398,399],{"class":103},"seek",[79,401,107],{"class":96},[79,403,404],{"class":110},"\"set\"",[79,406,407],{"class":96},")               ",[79,409,410],{"class":85},"-- rewind to start\n",[79,412,413,415,418,420,422,424,426,428,431,434],{"class":81,"line":210},[79,414,93],{"class":92},[79,416,417],{"class":96}," line1 ",[79,419,100],{"class":92},[79,421,372],{"class":371},[79,423,375],{"class":96},[79,425,378],{"class":103},[79,427,107],{"class":96},[79,429,430],{"class":110},"\"*l\"",[79,432,433],{"class":96},")  ",[79,435,436],{"class":85},"-- first line (no \\n)\n",[79,438,439,441,443,445,447,449],{"class":81,"line":216},[79,440,394],{"class":371},[79,442,375],{"class":96},[79,444,399],{"class":103},[79,446,107],{"class":96},[79,448,404],{"class":110},[79,450,120],{"class":96},[79,452,453,455,458,460,462,464,466,468,471,473],{"class":81,"line":222},[79,454,93],{"class":92},[79,456,457],{"class":96}," line1_with_nl ",[79,459,100],{"class":92},[79,461,372],{"class":371},[79,463,375],{"class":96},[79,465,378],{"class":103},[79,467,107],{"class":96},[79,469,470],{"class":110},"\"*L\"",[79,472,433],{"class":96},[79,474,475],{"class":85},"-- first line (with \\n)\n",[79,477,478,480,482,484,486,488],{"class":81,"line":228},[79,479,394],{"class":371},[79,481,375],{"class":96},[79,483,399],{"class":103},[79,485,107],{"class":96},[79,487,404],{"class":110},[79,489,120],{"class":96},[79,491,492,494,497,499,501,503,505,507,510,512],{"class":81,"line":234},[79,493,93],{"class":92},[79,495,496],{"class":96}," num ",[79,498,100],{"class":92},[79,500,372],{"class":371},[79,502,375],{"class":96},[79,504,378],{"class":103},[79,506,107],{"class":96},[79,508,509],{"class":110},"\"*n\"",[79,511,386],{"class":96},[79,513,514],{"class":85},"-- reads a leading number from file\n",[79,516,517,519,521,523,525,527],{"class":81,"line":240},[79,518,394],{"class":371},[79,520,375],{"class":96},[79,522,399],{"class":103},[79,524,107],{"class":96},[79,526,404],{"class":110},[79,528,120],{"class":96},[79,530,531,533,536,538,540,542,544,546,549,552],{"class":81,"line":245},[79,532,93],{"class":92},[79,534,535],{"class":96}," chunk ",[79,537,100],{"class":92},[79,539,372],{"class":371},[79,541,375],{"class":96},[79,543,378],{"class":103},[79,545,107],{"class":96},[79,547,548],{"class":103},"100",[79,550,551],{"class":96},")    ",[79,553,554],{"class":85},"-- first 100 bytes\n",[79,556,557],{"class":81,"line":251},[79,558,177],{"emptyLinePlaceholder":176},[79,560,561],{"class":81,"line":257},[79,562,563],{"class":85},"-- Multiple reads in one call (each arg is a format):\n",[79,565,566,568,570,572,574,576],{"class":81,"line":263},[79,567,394],{"class":371},[79,569,375],{"class":96},[79,571,399],{"class":103},[79,573,107],{"class":96},[79,575,404],{"class":110},[79,577,120],{"class":96},[79,579,580,582,585,587,589,591,593,595,597,599,601,603,605,607],{"class":81,"line":269},[79,581,93],{"class":92},[79,583,584],{"class":96}," l1, l2, l3 ",[79,586,100],{"class":92},[79,588,372],{"class":371},[79,590,375],{"class":96},[79,592,378],{"class":103},[79,594,107],{"class":96},[79,596,430],{"class":110},[79,598,114],{"class":96},[79,600,430],{"class":110},[79,602,114],{"class":96},[79,604,430],{"class":110},[79,606,433],{"class":96},[79,608,609],{"class":85},"-- first 3 lines\n",[79,611,612,614,616,619],{"class":81,"line":275},[79,613,394],{"class":371},[79,615,375],{"class":96},[79,617,618],{"class":103},"close",[79,620,621],{"class":96},"()\n",[623,624,626],"h3",{"id":625},"production-memory-efficient-line-streaming","Production: Memory-efficient line streaming",[67,628,629],{"language":69},[71,630,632],{"className":73,"code":631,"language":69,"meta":75,"style":75},"-- f:lines(format?): returns an iterator for line-by-line reading\n-- Does NOT load the whole file into memory — reads one line at a time\n-- CRITICAL for multi-GB files\n\nlocal function process_large_file(path, handler)\n  local f = assert(io.open(path, \"r\"))\n  for line in f:lines() do  -- one line at a time, constant memory\n    handler(line)\n  end\n  f:close()  -- ALWAYS close (even on error — use pcall or \u003Cclose>)\nend\n\nlocal count = 0\nprocess_large_file(\"huge.log\", function(line)\n  if line:match(\"ERROR\") then count = count + 1 end\nend)\nprint(\"error lines:\", count)\n\n-- With \u003Cclose> for guaranteed cleanup (Lua 5.4+):\nlocal function process_safe(path, handler)\n  local f \u003Cclose> = assert(io.open(path, \"r\"))  -- auto-close on scope exit (even on error)\n  for line in f:lines() do\n    handler(line)\n  end\nend\n",[19,633,634,639,644,649,653,666,688,715,723,728,743,747,751,763,780,819,826,839,843,848,859,885,904,910,914],{"__ignoreMap":75},[79,635,636],{"class":81,"line":82},[79,637,638],{"class":85},"-- f:lines(format?): returns an iterator for line-by-line reading\n",[79,640,641],{"class":81,"line":89},[79,642,643],{"class":85},"-- Does NOT load the whole file into memory — reads one line at a time\n",[79,645,646],{"class":81,"line":123},[79,647,648],{"class":85},"-- CRITICAL for multi-GB files\n",[79,650,651],{"class":81,"line":138},[79,652,177],{"emptyLinePlaceholder":176},[79,654,655,657,660,663],{"class":81,"line":167},[79,656,93],{"class":92},[79,658,659],{"class":92}," function",[79,661,662],{"class":371}," process_large_file",[79,664,665],{"class":96},"(path, handler)\n",[79,667,668,671,673,675,677,679,681,684,686],{"class":81,"line":173},[79,669,670],{"class":92},"  local",[79,672,132],{"class":96},[79,674,100],{"class":92},[79,676,306],{"class":103},[79,678,107],{"class":96},[79,680,21],{"class":103},[79,682,683],{"class":96},"(path, ",[79,685,117],{"class":110},[79,687,321],{"class":96},[79,689,690,693,696,699,701,703,706,709,712],{"class":81,"line":180},[79,691,692],{"class":92},"  for",[79,694,695],{"class":96}," line ",[79,697,698],{"class":92},"in",[79,700,372],{"class":371},[79,702,375],{"class":96},[79,704,705],{"class":103},"lines",[79,707,708],{"class":96},"() ",[79,710,711],{"class":92},"do",[79,713,714],{"class":85},"  -- one line at a time, constant memory\n",[79,716,717,720],{"class":81,"line":186},[79,718,719],{"class":103},"    handler",[79,721,722],{"class":96},"(line)\n",[79,724,725],{"class":81,"line":192},[79,726,727],{"class":92},"  end\n",[79,729,730,733,735,737,740],{"class":81,"line":198},[79,731,732],{"class":371},"  f",[79,734,375],{"class":96},[79,736,618],{"class":103},[79,738,739],{"class":96},"()  ",[79,741,742],{"class":85},"-- ALWAYS close (even on error — use pcall or \u003Cclose>)\n",[79,744,745],{"class":81,"line":204},[79,746,170],{"class":92},[79,748,749],{"class":81,"line":210},[79,750,177],{"emptyLinePlaceholder":176},[79,752,753,755,758,760],{"class":81,"line":216},[79,754,93],{"class":92},[79,756,757],{"class":96}," count ",[79,759,100],{"class":92},[79,761,762],{"class":103}," 0\n",[79,764,765,768,770,773,775,778],{"class":81,"line":222},[79,766,767],{"class":103},"process_large_file",[79,769,107],{"class":96},[79,771,772],{"class":110},"\"huge.log\"",[79,774,114],{"class":96},[79,776,777],{"class":92},"function",[79,779,722],{"class":96},[79,781,782,785,788,790,793,795,798,801,804,806,808,810,813,816],{"class":81,"line":228},[79,783,784],{"class":92},"  if",[79,786,787],{"class":371}," line",[79,789,375],{"class":96},[79,791,792],{"class":103},"match",[79,794,107],{"class":96},[79,796,797],{"class":110},"\"ERROR\"",[79,799,800],{"class":96},") ",[79,802,803],{"class":92},"then",[79,805,757],{"class":96},[79,807,100],{"class":92},[79,809,757],{"class":96},[79,811,812],{"class":92},"+",[79,814,815],{"class":103}," 1",[79,817,818],{"class":92}," end\n",[79,820,821,824],{"class":81,"line":234},[79,822,823],{"class":92},"end",[79,825,120],{"class":96},[79,827,828,831,833,836],{"class":81,"line":240},[79,829,830],{"class":103},"print",[79,832,107],{"class":96},[79,834,835],{"class":110},"\"error lines:\"",[79,837,838],{"class":96},", count)\n",[79,840,841],{"class":81,"line":245},[79,842,177],{"emptyLinePlaceholder":176},[79,844,845],{"class":81,"line":251},[79,846,847],{"class":85},"-- With \u003Cclose> for guaranteed cleanup (Lua 5.4+):\n",[79,849,850,852,854,857],{"class":81,"line":257},[79,851,93],{"class":92},[79,853,659],{"class":92},[79,855,856],{"class":371}," process_safe",[79,858,665],{"class":96},[79,860,861,863,865,867,870,872,874,876,878,880,882],{"class":81,"line":263},[79,862,670],{"class":92},[79,864,132],{"class":96},[79,866,42],{"class":92},[79,868,869],{"class":92}," =",[79,871,306],{"class":103},[79,873,107],{"class":96},[79,875,21],{"class":103},[79,877,683],{"class":96},[79,879,117],{"class":110},[79,881,161],{"class":96},[79,883,884],{"class":85},"-- auto-close on scope exit (even on error)\n",[79,886,887,889,891,893,895,897,899,901],{"class":81,"line":269},[79,888,692],{"class":92},[79,890,695],{"class":96},[79,892,698],{"class":92},[79,894,372],{"class":371},[79,896,375],{"class":96},[79,898,705],{"class":103},[79,900,708],{"class":96},[79,902,903],{"class":92},"do\n",[79,905,906,908],{"class":81,"line":275},[79,907,719],{"class":103},[79,909,722],{"class":96},[79,911,912],{"class":81,"line":281},[79,913,727],{"class":92},[79,915,917],{"class":81,"line":916},25,[79,918,170],{"class":92},[60,920,922],{"id":921},"writing","Writing",[67,924,925,1179,1183,1431,1435,4955,4972,4989,5068,5077,5081,5093,5285,5313,5329,5342,5357,5361],{"language":69},[71,926,928],{"className":73,"code":927,"language":69,"meta":75,"style":75},"local f = assert(io.open(\"output.txt\", \"w\"))\n\n-- write accepts multiple string arguments (no separator)\nf:write(\"hello\", \" \", \"world\", \"\\n\")  -- \"hello world\\n\"\nf:write(42)                          -- coerces number to string (but no format control)\n\n-- For formatted output, use string.format (then write the result)\nf:write(string.format(\"count: %d, rate: %.2f\\n\", 42, 3.14159))\n\n-- flush: force OS buffer to disk (before a crash, for critical data)\nf:flush()\n\nf:close()  -- close also flushes\n\n-- Append mode: writes go to end of file\nlocal a = assert(io.open(\"log.txt\", \"a\"))\na:write(os.date(\"%Y-%m-%d %H:%M:%S\"), \" log entry\\n\")\na:close()\n",[19,929,930,956,960,965,1004,1023,1027,1032,1065,1069,1074,1085,1089,1102,1106,1111,1138,1169],{"__ignoreMap":75},[79,931,932,934,936,938,940,942,944,946,949,951,954],{"class":81,"line":82},[79,933,93],{"class":92},[79,935,132],{"class":96},[79,937,100],{"class":92},[79,939,306],{"class":103},[79,941,107],{"class":96},[79,943,21],{"class":103},[79,945,107],{"class":96},[79,947,948],{"class":110},"\"output.txt\"",[79,950,114],{"class":96},[79,952,953],{"class":110},"\"w\"",[79,955,321],{"class":96},[79,957,958],{"class":81,"line":89},[79,959,177],{"emptyLinePlaceholder":176},[79,961,962],{"class":81,"line":123},[79,963,964],{"class":85},"-- write accepts multiple string arguments (no separator)\n",[79,966,967,969,971,974,976,979,981,984,986,989,991,994,997,999,1001],{"class":81,"line":138},[79,968,394],{"class":371},[79,970,375],{"class":96},[79,972,973],{"class":103},"write",[79,975,107],{"class":96},[79,977,978],{"class":110},"\"hello\"",[79,980,114],{"class":96},[79,982,983],{"class":110},"\" \"",[79,985,114],{"class":96},[79,987,988],{"class":110},"\"world\"",[79,990,114],{"class":96},[79,992,993],{"class":110},"\"",[79,995,996],{"class":103},"\\n",[79,998,993],{"class":110},[79,1000,433],{"class":96},[79,1002,1003],{"class":85},"-- \"hello world\\n\"\n",[79,1005,1006,1008,1010,1012,1014,1017,1020],{"class":81,"line":167},[79,1007,394],{"class":371},[79,1009,375],{"class":96},[79,1011,973],{"class":103},[79,1013,107],{"class":96},[79,1015,1016],{"class":103},"42",[79,1018,1019],{"class":96},")                          ",[79,1021,1022],{"class":85},"-- coerces number to string (but no format control)\n",[79,1024,1025],{"class":81,"line":173},[79,1026,177],{"emptyLinePlaceholder":176},[79,1028,1029],{"class":81,"line":180},[79,1030,1031],{"class":85},"-- For formatted output, use string.format (then write the result)\n",[79,1033,1034,1036,1038,1040,1042,1045,1047,1050,1052,1054,1056,1058,1060,1063],{"class":81,"line":186},[79,1035,394],{"class":371},[79,1037,375],{"class":96},[79,1039,973],{"class":103},[79,1041,107],{"class":96},[79,1043,1044],{"class":103},"string.format",[79,1046,107],{"class":96},[79,1048,1049],{"class":110},"\"count: %d, rate: %.2f",[79,1051,996],{"class":103},[79,1053,993],{"class":110},[79,1055,114],{"class":96},[79,1057,1016],{"class":103},[79,1059,114],{"class":96},[79,1061,1062],{"class":103},"3.14159",[79,1064,321],{"class":96},[79,1066,1067],{"class":81,"line":192},[79,1068,177],{"emptyLinePlaceholder":176},[79,1070,1071],{"class":81,"line":198},[79,1072,1073],{"class":85},"-- flush: force OS buffer to disk (before a crash, for critical data)\n",[79,1075,1076,1078,1080,1083],{"class":81,"line":204},[79,1077,394],{"class":371},[79,1079,375],{"class":96},[79,1081,1082],{"class":103},"flush",[79,1084,621],{"class":96},[79,1086,1087],{"class":81,"line":210},[79,1088,177],{"emptyLinePlaceholder":176},[79,1090,1091,1093,1095,1097,1099],{"class":81,"line":216},[79,1092,394],{"class":371},[79,1094,375],{"class":96},[79,1096,618],{"class":103},[79,1098,739],{"class":96},[79,1100,1101],{"class":85},"-- close also flushes\n",[79,1103,1104],{"class":81,"line":222},[79,1105,177],{"emptyLinePlaceholder":176},[79,1107,1108],{"class":81,"line":228},[79,1109,1110],{"class":85},"-- Append mode: writes go to end of file\n",[79,1112,1113,1115,1118,1120,1122,1124,1126,1128,1131,1133,1136],{"class":81,"line":234},[79,1114,93],{"class":92},[79,1116,1117],{"class":96}," a ",[79,1119,100],{"class":92},[79,1121,306],{"class":103},[79,1123,107],{"class":96},[79,1125,21],{"class":103},[79,1127,107],{"class":96},[79,1129,1130],{"class":110},"\"log.txt\"",[79,1132,114],{"class":96},[79,1134,1135],{"class":110},"\"a\"",[79,1137,321],{"class":96},[79,1139,1140,1143,1145,1147,1149,1152,1154,1157,1160,1163,1165,1167],{"class":81,"line":240},[79,1141,1142],{"class":371},"a",[79,1144,375],{"class":96},[79,1146,973],{"class":103},[79,1148,107],{"class":96},[79,1150,1151],{"class":103},"os.date",[79,1153,107],{"class":96},[79,1155,1156],{"class":110},"\"%Y-%m-%d %H:%M:%S\"",[79,1158,1159],{"class":96},"), ",[79,1161,1162],{"class":110},"\" log entry",[79,1164,996],{"class":103},[79,1166,993],{"class":110},[79,1168,120],{"class":96},[79,1170,1171,1173,1175,1177],{"class":81,"line":245},[79,1172,1142],{"class":371},[79,1174,375],{"class":96},[79,1176,618],{"class":103},[79,1178,621],{"class":96},[623,1180,1182],{"id":1181},"production-atomic-write-temp-file-rename","Production: Atomic write (temp file + rename)",[67,1184,1185],{"language":69},[71,1186,1188],{"className":73,"code":1187,"language":69,"meta":75,"style":75},"-- Write to a temp file, then rename — prevents corruption if the process\n-- crashes mid-write (rename is atomic on POSIX)\nlocal function atomic_write(path, content)\n  local tmp = path .. \".tmp.\" .. tostring(os.time())\n  local f, err = io.open(tmp, \"w\")\n  if not f then return nil, err end\n  local ok, write_err = pcall(function()\n    f:write(content)\n    f:flush()  -- ensure data hits disk before rename\n    f:close()\n  end)\n  if not ok then\n    f:close()\n    os.remove(tmp)  -- cleanup temp file\n    return nil, write_err\n  end\n  os.rename(tmp, path)  -- atomic on POSIX (not guaranteed on Windows)\n  return true\nend\n\natomic_write(\"config.json\", json_data)\n-- If the process crashes during write, config.json is untouched (temp file orphaned)\n",[19,1189,1190,1195,1200,1212,1242,1259,1280,1298,1310,1323,1333,1340,1351,1361,1372,1382,1386,1397,1405,1409,1413,1426],{"__ignoreMap":75},[79,1191,1192],{"class":81,"line":82},[79,1193,1194],{"class":85},"-- Write to a temp file, then rename — prevents corruption if the process\n",[79,1196,1197],{"class":81,"line":89},[79,1198,1199],{"class":85},"-- crashes mid-write (rename is atomic on POSIX)\n",[79,1201,1202,1204,1206,1209],{"class":81,"line":123},[79,1203,93],{"class":92},[79,1205,659],{"class":92},[79,1207,1208],{"class":371}," atomic_write",[79,1210,1211],{"class":96},"(path, content)\n",[79,1213,1214,1216,1219,1221,1224,1226,1229,1231,1234,1236,1239],{"class":81,"line":138},[79,1215,670],{"class":92},[79,1217,1218],{"class":96}," tmp ",[79,1220,100],{"class":92},[79,1222,1223],{"class":96}," path ",[79,1225,149],{"class":92},[79,1227,1228],{"class":110}," \".tmp.\" ",[79,1230,149],{"class":92},[79,1232,1233],{"class":103}," tostring",[79,1235,107],{"class":96},[79,1237,1238],{"class":103},"os.time",[79,1240,1241],{"class":96},"())\n",[79,1243,1244,1246,1248,1250,1252,1255,1257],{"class":81,"line":167},[79,1245,670],{"class":92},[79,1247,97],{"class":96},[79,1249,100],{"class":92},[79,1251,104],{"class":103},[79,1253,1254],{"class":96},"(tmp, ",[79,1256,953],{"class":110},[79,1258,120],{"class":96},[79,1260,1261,1263,1265,1267,1269,1272,1275,1278],{"class":81,"line":173},[79,1262,784],{"class":92},[79,1264,129],{"class":92},[79,1266,132],{"class":96},[79,1268,803],{"class":92},[79,1270,1271],{"class":92}," return",[79,1273,1274],{"class":103}," nil",[79,1276,1277],{"class":96},", err ",[79,1279,170],{"class":92},[79,1281,1282,1284,1287,1289,1292,1294,1296],{"class":81,"line":180},[79,1283,670],{"class":92},[79,1285,1286],{"class":96}," ok, write_err ",[79,1288,100],{"class":92},[79,1290,1291],{"class":103}," pcall",[79,1293,107],{"class":96},[79,1295,777],{"class":92},[79,1297,621],{"class":96},[79,1299,1300,1303,1305,1307],{"class":81,"line":186},[79,1301,1302],{"class":371},"    f",[79,1304,375],{"class":96},[79,1306,973],{"class":103},[79,1308,1309],{"class":96},"(content)\n",[79,1311,1312,1314,1316,1318,1320],{"class":81,"line":192},[79,1313,1302],{"class":371},[79,1315,375],{"class":96},[79,1317,1082],{"class":103},[79,1319,739],{"class":96},[79,1321,1322],{"class":85},"-- ensure data hits disk before rename\n",[79,1324,1325,1327,1329,1331],{"class":81,"line":198},[79,1326,1302],{"class":371},[79,1328,375],{"class":96},[79,1330,618],{"class":103},[79,1332,621],{"class":96},[79,1334,1335,1338],{"class":81,"line":204},[79,1336,1337],{"class":92},"  end",[79,1339,120],{"class":96},[79,1341,1342,1344,1346,1349],{"class":81,"line":210},[79,1343,784],{"class":92},[79,1345,129],{"class":92},[79,1347,1348],{"class":96}," ok ",[79,1350,135],{"class":92},[79,1352,1353,1355,1357,1359],{"class":81,"line":216},[79,1354,1302],{"class":371},[79,1356,375],{"class":96},[79,1358,618],{"class":103},[79,1360,621],{"class":96},[79,1362,1363,1366,1369],{"class":81,"line":222},[79,1364,1365],{"class":103},"    os.remove",[79,1367,1368],{"class":96},"(tmp)  ",[79,1370,1371],{"class":85},"-- cleanup temp file\n",[79,1373,1374,1377,1379],{"class":81,"line":228},[79,1375,1376],{"class":92},"    return",[79,1378,1274],{"class":103},[79,1380,1381],{"class":96},", write_err\n",[79,1383,1384],{"class":81,"line":234},[79,1385,727],{"class":92},[79,1387,1388,1391,1394],{"class":81,"line":240},[79,1389,1390],{"class":103},"  os.rename",[79,1392,1393],{"class":96},"(tmp, path)  ",[79,1395,1396],{"class":85},"-- atomic on POSIX (not guaranteed on Windows)\n",[79,1398,1399,1402],{"class":81,"line":245},[79,1400,1401],{"class":92},"  return",[79,1403,1404],{"class":103}," true\n",[79,1406,1407],{"class":81,"line":251},[79,1408,170],{"class":92},[79,1410,1411],{"class":81,"line":257},[79,1412,177],{"emptyLinePlaceholder":176},[79,1414,1415,1418,1420,1423],{"class":81,"line":263},[79,1416,1417],{"class":103},"atomic_write",[79,1419,107],{"class":96},[79,1421,1422],{"class":110},"\"config.json\"",[79,1424,1425],{"class":96},", json_data)\n",[79,1427,1428],{"class":81,"line":269},[79,1429,1430],{"class":85},"-- If the process crashes during write, config.json is untouched (temp file orphaned)\n",[60,1432,1434],{"id":1433},"seeking","Seeking",[67,1436,1437,1704,1708],{"language":69},[71,1438,1440],{"className":73,"code":1439,"language":69,"meta":75,"style":75},"-- f:seek(whence, offset):\n-- \"set\"  → from beginning (offset 0 = start)\n-- \"cur\"  → from current position (DEFAULT)\n-- \"end\"  → from end\n\nlocal f = assert(io.open(\"data.bin\", \"rb\"))\n\nf:seek(\"end\")             -- go to end\nlocal size = f:seek()      -- get current position (= file size, since we're at end)\nprint(\"file size:\", size)  -- bytes\n\nf:seek(\"set\", 0)          -- rewind to start\nf:seek(\"set\", 1024)       -- skip first 1KB\nlocal header = f:read(16) -- read 16 bytes at offset 1024\n\nf:seek(\"cur\", -4)         -- back up 4 bytes\nlocal reread = f:read(4)   -- re-read those 4 bytes\n\nf:close()\n\n-- NOTE: seeking doesn't work on pipes (io.popen), stdin, or special files\n",[19,1441,1442,1447,1452,1457,1462,1466,1492,1496,1515,1536,1551,1555,1577,1600,1625,1629,1656,1681,1685,1695,1699],{"__ignoreMap":75},[79,1443,1444],{"class":81,"line":82},[79,1445,1446],{"class":85},"-- f:seek(whence, offset):\n",[79,1448,1449],{"class":81,"line":89},[79,1450,1451],{"class":85},"-- \"set\"  → from beginning (offset 0 = start)\n",[79,1453,1454],{"class":81,"line":123},[79,1455,1456],{"class":85},"-- \"cur\"  → from current position (DEFAULT)\n",[79,1458,1459],{"class":81,"line":138},[79,1460,1461],{"class":85},"-- \"end\"  → from end\n",[79,1463,1464],{"class":81,"line":167},[79,1465,177],{"emptyLinePlaceholder":176},[79,1467,1468,1470,1472,1474,1476,1478,1480,1482,1485,1487,1490],{"class":81,"line":173},[79,1469,93],{"class":92},[79,1471,132],{"class":96},[79,1473,100],{"class":92},[79,1475,306],{"class":103},[79,1477,107],{"class":96},[79,1479,21],{"class":103},[79,1481,107],{"class":96},[79,1483,1484],{"class":110},"\"data.bin\"",[79,1486,114],{"class":96},[79,1488,1489],{"class":110},"\"rb\"",[79,1491,321],{"class":96},[79,1493,1494],{"class":81,"line":180},[79,1495,177],{"emptyLinePlaceholder":176},[79,1497,1498,1500,1502,1504,1506,1509,1512],{"class":81,"line":186},[79,1499,394],{"class":371},[79,1501,375],{"class":96},[79,1503,399],{"class":103},[79,1505,107],{"class":96},[79,1507,1508],{"class":110},"\"end\"",[79,1510,1511],{"class":96},")             ",[79,1513,1514],{"class":85},"-- go to end\n",[79,1516,1517,1519,1522,1524,1526,1528,1530,1533],{"class":81,"line":192},[79,1518,93],{"class":92},[79,1520,1521],{"class":96}," size ",[79,1523,100],{"class":92},[79,1525,372],{"class":371},[79,1527,375],{"class":96},[79,1529,399],{"class":103},[79,1531,1532],{"class":96},"()      ",[79,1534,1535],{"class":85},"-- get current position (= file size, since we're at end)\n",[79,1537,1538,1540,1542,1545,1548],{"class":81,"line":198},[79,1539,830],{"class":103},[79,1541,107],{"class":96},[79,1543,1544],{"class":110},"\"file size:\"",[79,1546,1547],{"class":96},", size)  ",[79,1549,1550],{"class":85},"-- bytes\n",[79,1552,1553],{"class":81,"line":204},[79,1554,177],{"emptyLinePlaceholder":176},[79,1556,1557,1559,1561,1563,1565,1567,1569,1572,1575],{"class":81,"line":210},[79,1558,394],{"class":371},[79,1560,375],{"class":96},[79,1562,399],{"class":103},[79,1564,107],{"class":96},[79,1566,404],{"class":110},[79,1568,114],{"class":96},[79,1570,1571],{"class":103},"0",[79,1573,1574],{"class":96},")          ",[79,1576,410],{"class":85},[79,1578,1579,1581,1583,1585,1587,1589,1591,1594,1597],{"class":81,"line":216},[79,1580,394],{"class":371},[79,1582,375],{"class":96},[79,1584,399],{"class":103},[79,1586,107],{"class":96},[79,1588,404],{"class":110},[79,1590,114],{"class":96},[79,1592,1593],{"class":103},"1024",[79,1595,1596],{"class":96},")       ",[79,1598,1599],{"class":85},"-- skip first 1KB\n",[79,1601,1602,1604,1607,1609,1611,1613,1615,1617,1620,1622],{"class":81,"line":222},[79,1603,93],{"class":92},[79,1605,1606],{"class":96}," header ",[79,1608,100],{"class":92},[79,1610,372],{"class":371},[79,1612,375],{"class":96},[79,1614,378],{"class":103},[79,1616,107],{"class":96},[79,1618,1619],{"class":103},"16",[79,1621,800],{"class":96},[79,1623,1624],{"class":85},"-- read 16 bytes at offset 1024\n",[79,1626,1627],{"class":81,"line":228},[79,1628,177],{"emptyLinePlaceholder":176},[79,1630,1631,1633,1635,1637,1639,1642,1644,1647,1650,1653],{"class":81,"line":234},[79,1632,394],{"class":371},[79,1634,375],{"class":96},[79,1636,399],{"class":103},[79,1638,107],{"class":96},[79,1640,1641],{"class":110},"\"cur\"",[79,1643,114],{"class":96},[79,1645,1646],{"class":92},"-",[79,1648,1649],{"class":103},"4",[79,1651,1652],{"class":96},")         ",[79,1654,1655],{"class":85},"-- back up 4 bytes\n",[79,1657,1658,1660,1663,1665,1667,1669,1671,1673,1675,1678],{"class":81,"line":240},[79,1659,93],{"class":92},[79,1661,1662],{"class":96}," reread ",[79,1664,100],{"class":92},[79,1666,372],{"class":371},[79,1668,375],{"class":96},[79,1670,378],{"class":103},[79,1672,107],{"class":96},[79,1674,1649],{"class":103},[79,1676,1677],{"class":96},")   ",[79,1679,1680],{"class":85},"-- re-read those 4 bytes\n",[79,1682,1683],{"class":81,"line":245},[79,1684,177],{"emptyLinePlaceholder":176},[79,1686,1687,1689,1691,1693],{"class":81,"line":251},[79,1688,394],{"class":371},[79,1690,375],{"class":96},[79,1692,618],{"class":103},[79,1694,621],{"class":96},[79,1696,1697],{"class":81,"line":257},[79,1698,177],{"emptyLinePlaceholder":176},[79,1700,1701],{"class":81,"line":263},[79,1702,1703],{"class":85},"-- NOTE: seeking doesn't work on pipes (io.popen), stdin, or special files\n",[60,1705,1707],{"id":1706},"binary-io","Binary I\u002FO",[67,1709,1710,2168,2172],{"language":69},[71,1711,1713],{"className":73,"code":1712,"language":69,"meta":75,"style":75},"-- Binary mode: read\u002Fwrite exact bytes, no line-ending translation\n-- Use string.byte\u002Fstring.char for byte-level access\n\nlocal f = assert(io.open(\"image.png\", \"rb\"))\nlocal header = f:read(8)  -- first 8 bytes (PNG signature)\n\n-- Check PNG signature: 137 80 78 71 13 10 26 10\nlocal sig = {137, 80, 78, 71, 13, 10, 26, 10}\nlocal valid = true\nfor i = 1, 8 do\n  if header:byte(i) ~= sig[i] then valid = false; break end\nend\nprint(\"valid PNG:\", valid)\n\nf:close()\n\n-- Write binary data:\nlocal out = assert(io.open(\"data.bin\", \"wb\"))\nout:write(string.char(0x89, 0x50, 0x4E, 0x47))  -- 4 raw bytes\nout:write(binary_blob)  -- string with embedded \\0 is fine in Lua\nout:close()\n\n-- Reading entire binary file:\nlocal f = assert(io.open(\"data.bin\", \"rb\"))\nlocal data = f:read(\"*a\")  -- entire file as a byte string\nf:close()\n\n-- Byte iteration:\nfor i = 1, #data do\n  local byte = data:byte(i)  -- 0-255\n  -- process byte\nend\n",[19,1714,1715,1720,1725,1729,1754,1778,1782,1787,1839,1850,1869,1907,1911,1923,1927,1937,1941,1946,1972,2010,2024,2034,2038,2043,2067,2091,2102,2107,2113,2134,2157,2163],{"__ignoreMap":75},[79,1716,1717],{"class":81,"line":82},[79,1718,1719],{"class":85},"-- Binary mode: read\u002Fwrite exact bytes, no line-ending translation\n",[79,1721,1722],{"class":81,"line":89},[79,1723,1724],{"class":85},"-- Use string.byte\u002Fstring.char for byte-level access\n",[79,1726,1727],{"class":81,"line":123},[79,1728,177],{"emptyLinePlaceholder":176},[79,1730,1731,1733,1735,1737,1739,1741,1743,1745,1748,1750,1752],{"class":81,"line":138},[79,1732,93],{"class":92},[79,1734,132],{"class":96},[79,1736,100],{"class":92},[79,1738,306],{"class":103},[79,1740,107],{"class":96},[79,1742,21],{"class":103},[79,1744,107],{"class":96},[79,1746,1747],{"class":110},"\"image.png\"",[79,1749,114],{"class":96},[79,1751,1489],{"class":110},[79,1753,321],{"class":96},[79,1755,1756,1758,1760,1762,1764,1766,1768,1770,1773,1775],{"class":81,"line":167},[79,1757,93],{"class":92},[79,1759,1606],{"class":96},[79,1761,100],{"class":92},[79,1763,372],{"class":371},[79,1765,375],{"class":96},[79,1767,378],{"class":103},[79,1769,107],{"class":96},[79,1771,1772],{"class":103},"8",[79,1774,433],{"class":96},[79,1776,1777],{"class":85},"-- first 8 bytes (PNG signature)\n",[79,1779,1780],{"class":81,"line":173},[79,1781,177],{"emptyLinePlaceholder":176},[79,1783,1784],{"class":81,"line":180},[79,1785,1786],{"class":85},"-- Check PNG signature: 137 80 78 71 13 10 26 10\n",[79,1788,1789,1791,1794,1796,1799,1802,1804,1807,1809,1812,1814,1817,1819,1822,1824,1827,1829,1832,1834,1836],{"class":81,"line":186},[79,1790,93],{"class":92},[79,1792,1793],{"class":96}," sig ",[79,1795,100],{"class":92},[79,1797,1798],{"class":96}," {",[79,1800,1801],{"class":103},"137",[79,1803,114],{"class":96},[79,1805,1806],{"class":103},"80",[79,1808,114],{"class":96},[79,1810,1811],{"class":103},"78",[79,1813,114],{"class":96},[79,1815,1816],{"class":103},"71",[79,1818,114],{"class":96},[79,1820,1821],{"class":103},"13",[79,1823,114],{"class":96},[79,1825,1826],{"class":103},"10",[79,1828,114],{"class":96},[79,1830,1831],{"class":103},"26",[79,1833,114],{"class":96},[79,1835,1826],{"class":103},[79,1837,1838],{"class":96},"}\n",[79,1840,1841,1843,1846,1848],{"class":81,"line":192},[79,1842,93],{"class":92},[79,1844,1845],{"class":96}," valid ",[79,1847,100],{"class":92},[79,1849,1404],{"class":103},[79,1851,1852,1855,1858,1860,1862,1864,1866],{"class":81,"line":198},[79,1853,1854],{"class":92},"for",[79,1856,1857],{"class":96}," i ",[79,1859,100],{"class":92},[79,1861,815],{"class":103},[79,1863,114],{"class":96},[79,1865,1772],{"class":103},[79,1867,1868],{"class":92}," do\n",[79,1870,1871,1873,1876,1878,1881,1884,1887,1890,1892,1894,1896,1899,1902,1905],{"class":81,"line":204},[79,1872,784],{"class":92},[79,1874,1875],{"class":371}," header",[79,1877,375],{"class":96},[79,1879,1880],{"class":103},"byte",[79,1882,1883],{"class":96},"(i) ",[79,1885,1886],{"class":92},"~=",[79,1888,1889],{"class":96}," sig[i] ",[79,1891,803],{"class":92},[79,1893,1845],{"class":96},[79,1895,100],{"class":92},[79,1897,1898],{"class":103}," false",[79,1900,1901],{"class":96},"; ",[79,1903,1904],{"class":92},"break",[79,1906,818],{"class":92},[79,1908,1909],{"class":81,"line":210},[79,1910,170],{"class":92},[79,1912,1913,1915,1917,1920],{"class":81,"line":216},[79,1914,830],{"class":103},[79,1916,107],{"class":96},[79,1918,1919],{"class":110},"\"valid PNG:\"",[79,1921,1922],{"class":96},", valid)\n",[79,1924,1925],{"class":81,"line":222},[79,1926,177],{"emptyLinePlaceholder":176},[79,1928,1929,1931,1933,1935],{"class":81,"line":228},[79,1930,394],{"class":371},[79,1932,375],{"class":96},[79,1934,618],{"class":103},[79,1936,621],{"class":96},[79,1938,1939],{"class":81,"line":234},[79,1940,177],{"emptyLinePlaceholder":176},[79,1942,1943],{"class":81,"line":240},[79,1944,1945],{"class":85},"-- Write binary data:\n",[79,1947,1948,1950,1953,1955,1957,1959,1961,1963,1965,1967,1970],{"class":81,"line":245},[79,1949,93],{"class":92},[79,1951,1952],{"class":96}," out ",[79,1954,100],{"class":92},[79,1956,306],{"class":103},[79,1958,107],{"class":96},[79,1960,21],{"class":103},[79,1962,107],{"class":96},[79,1964,1484],{"class":110},[79,1966,114],{"class":96},[79,1968,1969],{"class":110},"\"wb\"",[79,1971,321],{"class":96},[79,1973,1974,1977,1979,1981,1983,1985,1987,1990,1992,1995,1997,2000,2002,2005,2007],{"class":81,"line":251},[79,1975,1976],{"class":371},"out",[79,1978,375],{"class":96},[79,1980,973],{"class":103},[79,1982,107],{"class":96},[79,1984,49],{"class":103},[79,1986,107],{"class":96},[79,1988,1989],{"class":103},"0x89",[79,1991,114],{"class":96},[79,1993,1994],{"class":103},"0x50",[79,1996,114],{"class":96},[79,1998,1999],{"class":103},"0x4E",[79,2001,114],{"class":96},[79,2003,2004],{"class":103},"0x47",[79,2006,161],{"class":96},[79,2008,2009],{"class":85},"-- 4 raw bytes\n",[79,2011,2012,2014,2016,2018,2021],{"class":81,"line":257},[79,2013,1976],{"class":371},[79,2015,375],{"class":96},[79,2017,973],{"class":103},[79,2019,2020],{"class":96},"(binary_blob)  ",[79,2022,2023],{"class":85},"-- string with embedded \\0 is fine in Lua\n",[79,2025,2026,2028,2030,2032],{"class":81,"line":263},[79,2027,1976],{"class":371},[79,2029,375],{"class":96},[79,2031,618],{"class":103},[79,2033,621],{"class":96},[79,2035,2036],{"class":81,"line":269},[79,2037,177],{"emptyLinePlaceholder":176},[79,2039,2040],{"class":81,"line":275},[79,2041,2042],{"class":85},"-- Reading entire binary file:\n",[79,2044,2045,2047,2049,2051,2053,2055,2057,2059,2061,2063,2065],{"class":81,"line":281},[79,2046,93],{"class":92},[79,2048,132],{"class":96},[79,2050,100],{"class":92},[79,2052,306],{"class":103},[79,2054,107],{"class":96},[79,2056,21],{"class":103},[79,2058,107],{"class":96},[79,2060,1484],{"class":110},[79,2062,114],{"class":96},[79,2064,1489],{"class":110},[79,2066,321],{"class":96},[79,2068,2069,2071,2074,2076,2078,2080,2082,2084,2086,2088],{"class":81,"line":916},[79,2070,93],{"class":92},[79,2072,2073],{"class":96}," data ",[79,2075,100],{"class":92},[79,2077,372],{"class":371},[79,2079,375],{"class":96},[79,2081,378],{"class":103},[79,2083,107],{"class":96},[79,2085,383],{"class":110},[79,2087,433],{"class":96},[79,2089,2090],{"class":85},"-- entire file as a byte string\n",[79,2092,2094,2096,2098,2100],{"class":81,"line":2093},26,[79,2095,394],{"class":371},[79,2097,375],{"class":96},[79,2099,618],{"class":103},[79,2101,621],{"class":96},[79,2103,2105],{"class":81,"line":2104},27,[79,2106,177],{"emptyLinePlaceholder":176},[79,2108,2110],{"class":81,"line":2109},28,[79,2111,2112],{"class":85},"-- Byte iteration:\n",[79,2114,2116,2118,2120,2122,2124,2126,2129,2132],{"class":81,"line":2115},29,[79,2117,1854],{"class":92},[79,2119,1857],{"class":96},[79,2121,100],{"class":92},[79,2123,815],{"class":103},[79,2125,114],{"class":96},[79,2127,2128],{"class":92},"#",[79,2130,2131],{"class":96},"data ",[79,2133,903],{"class":92},[79,2135,2137,2139,2142,2144,2147,2149,2151,2154],{"class":81,"line":2136},30,[79,2138,670],{"class":92},[79,2140,2141],{"class":96}," byte ",[79,2143,100],{"class":92},[79,2145,2146],{"class":371}," data",[79,2148,375],{"class":96},[79,2150,1880],{"class":103},[79,2152,2153],{"class":96},"(i)  ",[79,2155,2156],{"class":85},"-- 0-255\n",[79,2158,2160],{"class":81,"line":2159},31,[79,2161,2162],{"class":85},"  -- process byte\n",[79,2164,2166],{"class":81,"line":2165},32,[79,2167,170],{"class":92},[623,2169,2171],{"id":2170},"production-struct-binary-reader","Production: Struct binary reader",[67,2173,2174,2899,2907],{"language":69},[71,2175,2177],{"className":73,"code":2176,"language":69,"meta":75,"style":75},"-- Binary protocol reader with cursor tracking\nlocal BinaryReader = {}\nBinaryReader.__index = BinaryReader\n\nfunction BinaryReader.new(data)\n  return setmetatable({data = data, pos = 1}, BinaryReader)\nend\n\nfunction BinaryReader:u8()   -- unsigned 8-bit\n  local v = self.data:byte(self.pos)\n  self.pos = self.pos + 1\n  return v\nend\n\nfunction BinaryReader:u16be()  -- big-endian 16-bit\n  local b1, b2 = self.data:byte(self.pos, self.pos + 1)\n  self.pos = self.pos + 2\n  return b1 * 256 + b2\nend\n\nfunction BinaryReader:u32le()  -- little-endian 32-bit\n  local b1, b2, b3, b4 = self.data:byte(self.pos, self.pos + 3)\n  self.pos = self.pos + 4\n  return b1 + b2 * 256 + b3 * 65536 + b4 * 16777216\nend\n\nfunction BinaryReader:bytes(n)\n  local s = self.data:sub(self.pos, self.pos + n - 1)\n  self.pos = self.pos + n\n  return s\nend\n\nfunction BinaryReader:remaining()\n  return #self.data - self.pos + 1\nend\n\n-- Usage: parse a binary protocol header\nlocal function parse_packet(data)\n  local r = BinaryReader.new(data)\n  local magic = r:bytes(4)       -- 4-byte magic\n  local version = r:u8()         -- 1-byte version\n  local flags = r:u16be()        -- 2-byte flags (big-endian)\n  local length = r:u32le()       -- 4-byte length (little-endian)\n  local payload = r:bytes(length)\n  return {magic = magic, version = version, flags = flags, payload = payload}\nend\n",[19,2178,2179,2184,2196,2209,2213,2228,2250,2254,2258,2275,2308,2331,2338,2342,2346,2362,2403,2424,2442,2446,2450,2466,2508,2529,2564,2568,2572,2586,2633,2654,2661,2665,2669,2683,2710,2715,2720,2726,2738,2755,2781,2803,2825,2847,2866,2894],{"__ignoreMap":75},[79,2180,2181],{"class":81,"line":82},[79,2182,2183],{"class":85},"-- Binary protocol reader with cursor tracking\n",[79,2185,2186,2188,2191,2193],{"class":81,"line":89},[79,2187,93],{"class":92},[79,2189,2190],{"class":96}," BinaryReader ",[79,2192,100],{"class":92},[79,2194,2195],{"class":96}," {}\n",[79,2197,2198,2201,2204,2206],{"class":81,"line":123},[79,2199,2200],{"class":96},"BinaryReader.",[79,2202,2203],{"class":371},"__index",[79,2205,869],{"class":92},[79,2207,2208],{"class":96}," BinaryReader\n",[79,2210,2211],{"class":81,"line":138},[79,2212,177],{"emptyLinePlaceholder":176},[79,2214,2215,2217,2220,2222,2225],{"class":81,"line":167},[79,2216,777],{"class":92},[79,2218,2219],{"class":371}," BinaryReader",[79,2221,58],{"class":96},[79,2223,2224],{"class":371},"new",[79,2226,2227],{"class":96},"(data)\n",[79,2229,2230,2232,2235,2238,2240,2243,2245,2247],{"class":81,"line":173},[79,2231,1401],{"class":92},[79,2233,2234],{"class":103}," setmetatable",[79,2236,2237],{"class":96},"({data ",[79,2239,100],{"class":92},[79,2241,2242],{"class":96}," data, pos ",[79,2244,100],{"class":92},[79,2246,815],{"class":103},[79,2248,2249],{"class":96},"}, BinaryReader)\n",[79,2251,2252],{"class":81,"line":180},[79,2253,170],{"class":92},[79,2255,2256],{"class":81,"line":186},[79,2257,177],{"emptyLinePlaceholder":176},[79,2259,2260,2262,2264,2266,2269,2272],{"class":81,"line":192},[79,2261,777],{"class":92},[79,2263,2219],{"class":371},[79,2265,375],{"class":96},[79,2267,2268],{"class":371},"u8",[79,2270,2271],{"class":96},"()   ",[79,2273,2274],{"class":85},"-- unsigned 8-bit\n",[79,2276,2277,2279,2282,2284,2287,2289,2292,2294,2296,2298,2301,2303,2306],{"class":81,"line":198},[79,2278,670],{"class":92},[79,2280,2281],{"class":96}," v ",[79,2283,100],{"class":92},[79,2285,2286],{"class":103}," self",[79,2288,58],{"class":96},[79,2290,2291],{"class":371},"data",[79,2293,375],{"class":96},[79,2295,1880],{"class":103},[79,2297,107],{"class":96},[79,2299,2300],{"class":103},"self",[79,2302,58],{"class":96},[79,2304,2305],{"class":371},"pos",[79,2307,120],{"class":96},[79,2309,2310,2313,2315,2317,2319,2321,2323,2325,2328],{"class":81,"line":204},[79,2311,2312],{"class":103},"  self",[79,2314,58],{"class":96},[79,2316,2305],{"class":371},[79,2318,869],{"class":92},[79,2320,2286],{"class":103},[79,2322,58],{"class":96},[79,2324,2305],{"class":371},[79,2326,2327],{"class":92}," +",[79,2329,2330],{"class":103}," 1\n",[79,2332,2333,2335],{"class":81,"line":210},[79,2334,1401],{"class":92},[79,2336,2337],{"class":96}," v\n",[79,2339,2340],{"class":81,"line":216},[79,2341,170],{"class":92},[79,2343,2344],{"class":81,"line":222},[79,2345,177],{"emptyLinePlaceholder":176},[79,2347,2348,2350,2352,2354,2357,2359],{"class":81,"line":228},[79,2349,777],{"class":92},[79,2351,2219],{"class":371},[79,2353,375],{"class":96},[79,2355,2356],{"class":371},"u16be",[79,2358,739],{"class":96},[79,2360,2361],{"class":85},"-- big-endian 16-bit\n",[79,2363,2364,2366,2369,2371,2373,2375,2377,2379,2381,2383,2385,2387,2389,2391,2393,2395,2397,2399,2401],{"class":81,"line":234},[79,2365,670],{"class":92},[79,2367,2368],{"class":96}," b1, b2 ",[79,2370,100],{"class":92},[79,2372,2286],{"class":103},[79,2374,58],{"class":96},[79,2376,2291],{"class":371},[79,2378,375],{"class":96},[79,2380,1880],{"class":103},[79,2382,107],{"class":96},[79,2384,2300],{"class":103},[79,2386,58],{"class":96},[79,2388,2305],{"class":371},[79,2390,114],{"class":96},[79,2392,2300],{"class":103},[79,2394,58],{"class":96},[79,2396,2305],{"class":371},[79,2398,2327],{"class":92},[79,2400,815],{"class":103},[79,2402,120],{"class":96},[79,2404,2405,2407,2409,2411,2413,2415,2417,2419,2421],{"class":81,"line":240},[79,2406,2312],{"class":103},[79,2408,58],{"class":96},[79,2410,2305],{"class":371},[79,2412,869],{"class":92},[79,2414,2286],{"class":103},[79,2416,58],{"class":96},[79,2418,2305],{"class":371},[79,2420,2327],{"class":92},[79,2422,2423],{"class":103}," 2\n",[79,2425,2426,2428,2431,2434,2437,2439],{"class":81,"line":245},[79,2427,1401],{"class":92},[79,2429,2430],{"class":96}," b1 ",[79,2432,2433],{"class":92},"*",[79,2435,2436],{"class":103}," 256",[79,2438,2327],{"class":92},[79,2440,2441],{"class":96}," b2\n",[79,2443,2444],{"class":81,"line":251},[79,2445,170],{"class":92},[79,2447,2448],{"class":81,"line":257},[79,2449,177],{"emptyLinePlaceholder":176},[79,2451,2452,2454,2456,2458,2461,2463],{"class":81,"line":263},[79,2453,777],{"class":92},[79,2455,2219],{"class":371},[79,2457,375],{"class":96},[79,2459,2460],{"class":371},"u32le",[79,2462,739],{"class":96},[79,2464,2465],{"class":85},"-- little-endian 32-bit\n",[79,2467,2468,2470,2473,2475,2477,2479,2481,2483,2485,2487,2489,2491,2493,2495,2497,2499,2501,2503,2506],{"class":81,"line":269},[79,2469,670],{"class":92},[79,2471,2472],{"class":96}," b1, b2, b3, b4 ",[79,2474,100],{"class":92},[79,2476,2286],{"class":103},[79,2478,58],{"class":96},[79,2480,2291],{"class":371},[79,2482,375],{"class":96},[79,2484,1880],{"class":103},[79,2486,107],{"class":96},[79,2488,2300],{"class":103},[79,2490,58],{"class":96},[79,2492,2305],{"class":371},[79,2494,114],{"class":96},[79,2496,2300],{"class":103},[79,2498,58],{"class":96},[79,2500,2305],{"class":371},[79,2502,2327],{"class":92},[79,2504,2505],{"class":103}," 3",[79,2507,120],{"class":96},[79,2509,2510,2512,2514,2516,2518,2520,2522,2524,2526],{"class":81,"line":275},[79,2511,2312],{"class":103},[79,2513,58],{"class":96},[79,2515,2305],{"class":371},[79,2517,869],{"class":92},[79,2519,2286],{"class":103},[79,2521,58],{"class":96},[79,2523,2305],{"class":371},[79,2525,2327],{"class":92},[79,2527,2528],{"class":103}," 4\n",[79,2530,2531,2533,2535,2537,2540,2542,2544,2546,2549,2551,2554,2556,2559,2561],{"class":81,"line":281},[79,2532,1401],{"class":92},[79,2534,2430],{"class":96},[79,2536,812],{"class":92},[79,2538,2539],{"class":96}," b2 ",[79,2541,2433],{"class":92},[79,2543,2436],{"class":103},[79,2545,2327],{"class":92},[79,2547,2548],{"class":96}," b3 ",[79,2550,2433],{"class":92},[79,2552,2553],{"class":103}," 65536",[79,2555,2327],{"class":92},[79,2557,2558],{"class":96}," b4 ",[79,2560,2433],{"class":92},[79,2562,2563],{"class":103}," 16777216\n",[79,2565,2566],{"class":81,"line":916},[79,2567,170],{"class":92},[79,2569,2570],{"class":81,"line":2093},[79,2571,177],{"emptyLinePlaceholder":176},[79,2573,2574,2576,2578,2580,2583],{"class":81,"line":2104},[79,2575,777],{"class":92},[79,2577,2219],{"class":371},[79,2579,375],{"class":96},[79,2581,2582],{"class":371},"bytes",[79,2584,2585],{"class":96},"(n)\n",[79,2587,2588,2590,2593,2595,2597,2599,2601,2603,2606,2608,2610,2612,2614,2616,2618,2620,2622,2624,2627,2629,2631],{"class":81,"line":2109},[79,2589,670],{"class":92},[79,2591,2592],{"class":96}," s ",[79,2594,100],{"class":92},[79,2596,2286],{"class":103},[79,2598,58],{"class":96},[79,2600,2291],{"class":371},[79,2602,375],{"class":96},[79,2604,2605],{"class":103},"sub",[79,2607,107],{"class":96},[79,2609,2300],{"class":103},[79,2611,58],{"class":96},[79,2613,2305],{"class":371},[79,2615,114],{"class":96},[79,2617,2300],{"class":103},[79,2619,58],{"class":96},[79,2621,2305],{"class":371},[79,2623,2327],{"class":92},[79,2625,2626],{"class":96}," n ",[79,2628,1646],{"class":92},[79,2630,815],{"class":103},[79,2632,120],{"class":96},[79,2634,2635,2637,2639,2641,2643,2645,2647,2649,2651],{"class":81,"line":2115},[79,2636,2312],{"class":103},[79,2638,58],{"class":96},[79,2640,2305],{"class":371},[79,2642,869],{"class":92},[79,2644,2286],{"class":103},[79,2646,58],{"class":96},[79,2648,2305],{"class":371},[79,2650,2327],{"class":92},[79,2652,2653],{"class":96}," n\n",[79,2655,2656,2658],{"class":81,"line":2136},[79,2657,1401],{"class":92},[79,2659,2660],{"class":96}," s\n",[79,2662,2663],{"class":81,"line":2159},[79,2664,170],{"class":92},[79,2666,2667],{"class":81,"line":2165},[79,2668,177],{"emptyLinePlaceholder":176},[79,2670,2672,2674,2676,2678,2681],{"class":81,"line":2671},33,[79,2673,777],{"class":92},[79,2675,2219],{"class":371},[79,2677,375],{"class":96},[79,2679,2680],{"class":371},"remaining",[79,2682,621],{"class":96},[79,2684,2686,2688,2691,2693,2695,2697,2700,2702,2704,2706,2708],{"class":81,"line":2685},34,[79,2687,1401],{"class":92},[79,2689,2690],{"class":92}," #",[79,2692,2300],{"class":103},[79,2694,58],{"class":96},[79,2696,2291],{"class":371},[79,2698,2699],{"class":92}," -",[79,2701,2286],{"class":103},[79,2703,58],{"class":96},[79,2705,2305],{"class":371},[79,2707,2327],{"class":92},[79,2709,2330],{"class":103},[79,2711,2713],{"class":81,"line":2712},35,[79,2714,170],{"class":92},[79,2716,2718],{"class":81,"line":2717},36,[79,2719,177],{"emptyLinePlaceholder":176},[79,2721,2723],{"class":81,"line":2722},37,[79,2724,2725],{"class":85},"-- Usage: parse a binary protocol header\n",[79,2727,2729,2731,2733,2736],{"class":81,"line":2728},38,[79,2730,93],{"class":92},[79,2732,659],{"class":92},[79,2734,2735],{"class":371}," parse_packet",[79,2737,2227],{"class":96},[79,2739,2741,2743,2746,2748,2751,2753],{"class":81,"line":2740},39,[79,2742,670],{"class":92},[79,2744,2745],{"class":96}," r ",[79,2747,100],{"class":92},[79,2749,2750],{"class":96}," BinaryReader.",[79,2752,2224],{"class":103},[79,2754,2227],{"class":96},[79,2756,2758,2760,2763,2765,2768,2770,2772,2774,2776,2778],{"class":81,"line":2757},40,[79,2759,670],{"class":92},[79,2761,2762],{"class":96}," magic ",[79,2764,100],{"class":92},[79,2766,2767],{"class":371}," r",[79,2769,375],{"class":96},[79,2771,2582],{"class":103},[79,2773,107],{"class":96},[79,2775,1649],{"class":103},[79,2777,1596],{"class":96},[79,2779,2780],{"class":85},"-- 4-byte magic\n",[79,2782,2784,2786,2789,2791,2793,2795,2797,2800],{"class":81,"line":2783},41,[79,2785,670],{"class":92},[79,2787,2788],{"class":96}," version ",[79,2790,100],{"class":92},[79,2792,2767],{"class":371},[79,2794,375],{"class":96},[79,2796,2268],{"class":103},[79,2798,2799],{"class":96},"()         ",[79,2801,2802],{"class":85},"-- 1-byte version\n",[79,2804,2806,2808,2811,2813,2815,2817,2819,2822],{"class":81,"line":2805},42,[79,2807,670],{"class":92},[79,2809,2810],{"class":96}," flags ",[79,2812,100],{"class":92},[79,2814,2767],{"class":371},[79,2816,375],{"class":96},[79,2818,2356],{"class":103},[79,2820,2821],{"class":96},"()        ",[79,2823,2824],{"class":85},"-- 2-byte flags (big-endian)\n",[79,2826,2828,2830,2833,2835,2837,2839,2841,2844],{"class":81,"line":2827},43,[79,2829,670],{"class":92},[79,2831,2832],{"class":96}," length ",[79,2834,100],{"class":92},[79,2836,2767],{"class":371},[79,2838,375],{"class":96},[79,2840,2460],{"class":103},[79,2842,2843],{"class":96},"()       ",[79,2845,2846],{"class":85},"-- 4-byte length (little-endian)\n",[79,2848,2850,2852,2855,2857,2859,2861,2863],{"class":81,"line":2849},44,[79,2851,670],{"class":92},[79,2853,2854],{"class":96}," payload ",[79,2856,100],{"class":92},[79,2858,2767],{"class":371},[79,2860,375],{"class":96},[79,2862,2582],{"class":103},[79,2864,2865],{"class":96},"(length)\n",[79,2867,2869,2871,2874,2876,2879,2881,2884,2886,2889,2891],{"class":81,"line":2868},45,[79,2870,1401],{"class":92},[79,2872,2873],{"class":96}," {magic ",[79,2875,100],{"class":92},[79,2877,2878],{"class":96}," magic, version ",[79,2880,100],{"class":92},[79,2882,2883],{"class":96}," version, flags ",[79,2885,100],{"class":92},[79,2887,2888],{"class":96}," flags, payload ",[79,2890,100],{"class":92},[79,2892,2893],{"class":96}," payload}\n",[79,2895,2897],{"class":81,"line":2896},46,[79,2898,170],{"class":92},[60,2900,2902,2903,2906],{"id":2901},"standard-streams-io-library","Standard Streams & ",[19,2904,2905],{},"io"," Library",[67,2908,2909,3139,3145],{"language":69},[71,2910,2912],{"className":73,"code":2911,"language":69,"meta":75,"style":75},"-- io.stdin, io.stdout, io.stderr: predefined file handles\nio.stdout:write(\"normal output\\n\")\nio.stderr:write(\"error output\\n\")  -- goes to stderr (separate from stdout)\n\n-- io.read(format): reads from stdin (default input)\nio.write(\"Enter name: \")  -- no newline, stays on prompt line\nlocal name = io.read(\"*l\")  -- read a line from stdin\n\n-- io.input()\u002Fio.output(): set default input\u002Foutput file (affects io.read\u002Fio.write)\nio.input(\"data.txt\")      -- subsequent io.read() reads from data.txt\nio.output(\"result.txt\")   -- subsequent io.write() writes to result.txt\n-- Restore to defaults:\nio.input(io.stdin)\nio.output(io.stdout)\n\n-- io.type(f): check if a file handle is open\nlocal f = io.open(\"x.txt\", \"r\")\nprint(io.type(f))  -- \"file\"\nf:close()\nprint(io.type(f))  -- \"closed file\"\n",[19,2913,2914,2919,2939,2962,2966,2971,2985,3006,3010,3015,3030,3045,3050,3061,3071,3075,3080,3101,3116,3126],{"__ignoreMap":75},[79,2915,2916],{"class":81,"line":82},[79,2917,2918],{"class":85},"-- io.stdin, io.stdout, io.stderr: predefined file handles\n",[79,2920,2921,2924,2926,2928,2930,2933,2935,2937],{"class":81,"line":89},[79,2922,2923],{"class":103},"io.stdout",[79,2925,375],{"class":96},[79,2927,973],{"class":103},[79,2929,107],{"class":96},[79,2931,2932],{"class":110},"\"normal output",[79,2934,996],{"class":103},[79,2936,993],{"class":110},[79,2938,120],{"class":96},[79,2940,2941,2944,2946,2948,2950,2953,2955,2957,2959],{"class":81,"line":123},[79,2942,2943],{"class":103},"io.stderr",[79,2945,375],{"class":96},[79,2947,973],{"class":103},[79,2949,107],{"class":96},[79,2951,2952],{"class":110},"\"error output",[79,2954,996],{"class":103},[79,2956,993],{"class":110},[79,2958,433],{"class":96},[79,2960,2961],{"class":85},"-- goes to stderr (separate from stdout)\n",[79,2963,2964],{"class":81,"line":138},[79,2965,177],{"emptyLinePlaceholder":176},[79,2967,2968],{"class":81,"line":167},[79,2969,2970],{"class":85},"-- io.read(format): reads from stdin (default input)\n",[79,2972,2973,2975,2977,2980,2982],{"class":81,"line":173},[79,2974,29],{"class":103},[79,2976,107],{"class":96},[79,2978,2979],{"class":110},"\"Enter name: \"",[79,2981,433],{"class":96},[79,2983,2984],{"class":85},"-- no newline, stays on prompt line\n",[79,2986,2987,2989,2992,2994,2997,2999,3001,3003],{"class":81,"line":180},[79,2988,93],{"class":92},[79,2990,2991],{"class":96}," name ",[79,2993,100],{"class":92},[79,2995,2996],{"class":103}," io.read",[79,2998,107],{"class":96},[79,3000,430],{"class":110},[79,3002,433],{"class":96},[79,3004,3005],{"class":85},"-- read a line from stdin\n",[79,3007,3008],{"class":81,"line":186},[79,3009,177],{"emptyLinePlaceholder":176},[79,3011,3012],{"class":81,"line":192},[79,3013,3014],{"class":85},"-- io.input()\u002Fio.output(): set default input\u002Foutput file (affects io.read\u002Fio.write)\n",[79,3016,3017,3020,3022,3024,3027],{"class":81,"line":198},[79,3018,3019],{"class":103},"io.input",[79,3021,107],{"class":96},[79,3023,111],{"class":110},[79,3025,3026],{"class":96},")      ",[79,3028,3029],{"class":85},"-- subsequent io.read() reads from data.txt\n",[79,3031,3032,3035,3037,3040,3042],{"class":81,"line":204},[79,3033,3034],{"class":103},"io.output",[79,3036,107],{"class":96},[79,3038,3039],{"class":110},"\"result.txt\"",[79,3041,1677],{"class":96},[79,3043,3044],{"class":85},"-- subsequent io.write() writes to result.txt\n",[79,3046,3047],{"class":81,"line":210},[79,3048,3049],{"class":85},"-- Restore to defaults:\n",[79,3051,3052,3054,3056,3059],{"class":81,"line":216},[79,3053,3019],{"class":103},[79,3055,107],{"class":96},[79,3057,3058],{"class":103},"io.stdin",[79,3060,120],{"class":96},[79,3062,3063,3065,3067,3069],{"class":81,"line":222},[79,3064,3034],{"class":103},[79,3066,107],{"class":96},[79,3068,2923],{"class":103},[79,3070,120],{"class":96},[79,3072,3073],{"class":81,"line":228},[79,3074,177],{"emptyLinePlaceholder":176},[79,3076,3077],{"class":81,"line":234},[79,3078,3079],{"class":85},"-- io.type(f): check if a file handle is open\n",[79,3081,3082,3084,3086,3088,3090,3092,3095,3097,3099],{"class":81,"line":240},[79,3083,93],{"class":92},[79,3085,132],{"class":96},[79,3087,100],{"class":92},[79,3089,104],{"class":103},[79,3091,107],{"class":96},[79,3093,3094],{"class":110},"\"x.txt\"",[79,3096,114],{"class":96},[79,3098,117],{"class":110},[79,3100,120],{"class":96},[79,3102,3103,3105,3107,3110,3113],{"class":81,"line":245},[79,3104,830],{"class":103},[79,3106,107],{"class":96},[79,3108,3109],{"class":103},"io.type",[79,3111,3112],{"class":96},"(f))  ",[79,3114,3115],{"class":85},"-- \"file\"\n",[79,3117,3118,3120,3122,3124],{"class":81,"line":251},[79,3119,394],{"class":371},[79,3121,375],{"class":96},[79,3123,618],{"class":103},[79,3125,621],{"class":96},[79,3127,3128,3130,3132,3134,3136],{"class":81,"line":257},[79,3129,830],{"class":103},[79,3131,107],{"class":96},[79,3133,3109],{"class":103},[79,3135,3112],{"class":96},[79,3137,3138],{"class":85},"-- \"closed file\"\n",[60,3140,3142,3144],{"id":3141},"iopopen-subprocess-communication",[19,3143,33],{}," — Subprocess Communication",[67,3146,3147,3426,3437],{"language":69},[71,3148,3150],{"className":73,"code":3149,"language":69,"meta":75,"style":75},"-- io.popen(command, mode): runs a command, returns a pipe to its stdin\u002Fstdout\n-- mode \"r\": read command's stdout\n-- mode \"w\": write to command's stdin\n\n-- Read command output:\nlocal pipe = assert(io.popen(\"ls -la\", \"r\"))\nlocal output = pipe:read(\"*a\")  -- entire stdout as a string\npipe:close()  -- returns the exit code (true\u002Fnil + exit_reason in 5.4+)\nprint(output)\n\n-- Line-by-line:\nlocal pipe = assert(io.popen(\"grep ERROR log.txt\", \"r\"))\nfor line in pipe:lines() do\n  print(line)\nend\npipe:close()\n\n-- Capture exit status (Lua 5.4+):\nlocal pipe = assert(io.popen(\"ls \u002Fnonexistent\", \"r\"))\nlocal output = pipe:read(\"*a\")\nlocal ok, reason, exit_code = pipe:close()\nprint(ok, reason, exit_code)  -- false \"exit\" 2 (command failed with exit code 2)\n\n-- SECURITY: NEVER pass user input to io.popen without sanitizing — command injection!\n-- BAD:\n-- io.popen(\"echo \" .. user_input)  -- user_input = \"$(rm -rf \u002F)\" → disaster\n-- GOOD: use a safe API or validate input strictly\n",[19,3151,3152,3157,3162,3167,3171,3176,3202,3227,3241,3248,3252,3257,3282,3300,3307,3311,3321,3325,3330,3355,3375,3392,3402,3406,3411,3416,3421],{"__ignoreMap":75},[79,3153,3154],{"class":81,"line":82},[79,3155,3156],{"class":85},"-- io.popen(command, mode): runs a command, returns a pipe to its stdin\u002Fstdout\n",[79,3158,3159],{"class":81,"line":89},[79,3160,3161],{"class":85},"-- mode \"r\": read command's stdout\n",[79,3163,3164],{"class":81,"line":123},[79,3165,3166],{"class":85},"-- mode \"w\": write to command's stdin\n",[79,3168,3169],{"class":81,"line":138},[79,3170,177],{"emptyLinePlaceholder":176},[79,3172,3173],{"class":81,"line":167},[79,3174,3175],{"class":85},"-- Read command output:\n",[79,3177,3178,3180,3183,3185,3187,3189,3191,3193,3196,3198,3200],{"class":81,"line":173},[79,3179,93],{"class":92},[79,3181,3182],{"class":96}," pipe ",[79,3184,100],{"class":92},[79,3186,306],{"class":103},[79,3188,107],{"class":96},[79,3190,33],{"class":103},[79,3192,107],{"class":96},[79,3194,3195],{"class":110},"\"ls -la\"",[79,3197,114],{"class":96},[79,3199,117],{"class":110},[79,3201,321],{"class":96},[79,3203,3204,3206,3209,3211,3214,3216,3218,3220,3222,3224],{"class":81,"line":180},[79,3205,93],{"class":92},[79,3207,3208],{"class":96}," output ",[79,3210,100],{"class":92},[79,3212,3213],{"class":371}," pipe",[79,3215,375],{"class":96},[79,3217,378],{"class":103},[79,3219,107],{"class":96},[79,3221,383],{"class":110},[79,3223,433],{"class":96},[79,3225,3226],{"class":85},"-- entire stdout as a string\n",[79,3228,3229,3232,3234,3236,3238],{"class":81,"line":186},[79,3230,3231],{"class":371},"pipe",[79,3233,375],{"class":96},[79,3235,618],{"class":103},[79,3237,739],{"class":96},[79,3239,3240],{"class":85},"-- returns the exit code (true\u002Fnil + exit_reason in 5.4+)\n",[79,3242,3243,3245],{"class":81,"line":192},[79,3244,830],{"class":103},[79,3246,3247],{"class":96},"(output)\n",[79,3249,3250],{"class":81,"line":198},[79,3251,177],{"emptyLinePlaceholder":176},[79,3253,3254],{"class":81,"line":204},[79,3255,3256],{"class":85},"-- Line-by-line:\n",[79,3258,3259,3261,3263,3265,3267,3269,3271,3273,3276,3278,3280],{"class":81,"line":210},[79,3260,93],{"class":92},[79,3262,3182],{"class":96},[79,3264,100],{"class":92},[79,3266,306],{"class":103},[79,3268,107],{"class":96},[79,3270,33],{"class":103},[79,3272,107],{"class":96},[79,3274,3275],{"class":110},"\"grep ERROR log.txt\"",[79,3277,114],{"class":96},[79,3279,117],{"class":110},[79,3281,321],{"class":96},[79,3283,3284,3286,3288,3290,3292,3294,3296,3298],{"class":81,"line":216},[79,3285,1854],{"class":92},[79,3287,695],{"class":96},[79,3289,698],{"class":92},[79,3291,3213],{"class":371},[79,3293,375],{"class":96},[79,3295,705],{"class":103},[79,3297,708],{"class":96},[79,3299,903],{"class":92},[79,3301,3302,3305],{"class":81,"line":222},[79,3303,3304],{"class":103},"  print",[79,3306,722],{"class":96},[79,3308,3309],{"class":81,"line":228},[79,3310,170],{"class":92},[79,3312,3313,3315,3317,3319],{"class":81,"line":234},[79,3314,3231],{"class":371},[79,3316,375],{"class":96},[79,3318,618],{"class":103},[79,3320,621],{"class":96},[79,3322,3323],{"class":81,"line":240},[79,3324,177],{"emptyLinePlaceholder":176},[79,3326,3327],{"class":81,"line":245},[79,3328,3329],{"class":85},"-- Capture exit status (Lua 5.4+):\n",[79,3331,3332,3334,3336,3338,3340,3342,3344,3346,3349,3351,3353],{"class":81,"line":251},[79,3333,93],{"class":92},[79,3335,3182],{"class":96},[79,3337,100],{"class":92},[79,3339,306],{"class":103},[79,3341,107],{"class":96},[79,3343,33],{"class":103},[79,3345,107],{"class":96},[79,3347,3348],{"class":110},"\"ls \u002Fnonexistent\"",[79,3350,114],{"class":96},[79,3352,117],{"class":110},[79,3354,321],{"class":96},[79,3356,3357,3359,3361,3363,3365,3367,3369,3371,3373],{"class":81,"line":257},[79,3358,93],{"class":92},[79,3360,3208],{"class":96},[79,3362,100],{"class":92},[79,3364,3213],{"class":371},[79,3366,375],{"class":96},[79,3368,378],{"class":103},[79,3370,107],{"class":96},[79,3372,383],{"class":110},[79,3374,120],{"class":96},[79,3376,3377,3379,3382,3384,3386,3388,3390],{"class":81,"line":263},[79,3378,93],{"class":92},[79,3380,3381],{"class":96}," ok, reason, exit_code ",[79,3383,100],{"class":92},[79,3385,3213],{"class":371},[79,3387,375],{"class":96},[79,3389,618],{"class":103},[79,3391,621],{"class":96},[79,3393,3394,3396,3399],{"class":81,"line":269},[79,3395,830],{"class":103},[79,3397,3398],{"class":96},"(ok, reason, exit_code)  ",[79,3400,3401],{"class":85},"-- false \"exit\" 2 (command failed with exit code 2)\n",[79,3403,3404],{"class":81,"line":275},[79,3405,177],{"emptyLinePlaceholder":176},[79,3407,3408],{"class":81,"line":281},[79,3409,3410],{"class":85},"-- SECURITY: NEVER pass user input to io.popen without sanitizing — command injection!\n",[79,3412,3413],{"class":81,"line":916},[79,3414,3415],{"class":85},"-- BAD:\n",[79,3417,3418],{"class":81,"line":2093},[79,3419,3420],{"class":85},"-- io.popen(\"echo \" .. user_input)  -- user_input = \"$(rm -rf \u002F)\" → disaster\n",[79,3422,3423],{"class":81,"line":2104},[79,3424,3425],{"class":85},"-- GOOD: use a safe API or validate input strictly\n",[60,3427,3429,3430,3432,3433,3436],{"id":3428},"directory-listing-no-stdlib-use-lfs-or-popen","Directory Listing (no stdlib — use ",[19,3431,53],{}," or ",[19,3434,3435],{},"popen",")",[67,3438,3439,3703,3707],{"language":69},[71,3440,3442],{"className":73,"code":3441,"language":69,"meta":75,"style":75},"-- Method 1: io.popen (cross-platform but shell-dependent)\nlocal function list_dir(dir)\n  local pipe = assert(io.popen('ls -1 \"' .. dir:gsub('\"', '\\\\\"') .. '\"', \"r\"))\n  local files = {}\n  for line in pipe:lines() do\n    files[#files + 1] = line\n  end\n  pipe:close()\n  return files\nend\n\n-- Method 2: luarocks install luafilesystem (lfs) — portable, no shell\n-- local lfs = require(\"lfs\")\n-- for entry in lfs.dir(\".\") do\n--   local attr = lfs.attributes(entry)  -- {mode = \"file\"\u002F\"directory\", size = N, ...}\n--   print(entry, attr.mode, attr.size)\n-- end\n\n-- Method 3: Recursive walk (with lfs)\n-- local function walk(dir, callback)\n--   for entry in lfs.dir(dir) do\n--     if entry ~= \".\" and entry ~= \"..\" then\n--       local path = dir .. \"\u002F\" .. entry\n--       local attr = lfs.attributes(path)\n--       if attr.mode == \"directory\" then\n--         walk(path, callback)\n--       else\n--         callback(path, attr)\n--       end\n--     end\n--   end\n-- end\n",[19,3443,3444,3449,3461,3519,3530,3548,3570,3574,3585,3592,3596,3600,3605,3610,3615,3620,3625,3630,3634,3639,3644,3649,3654,3659,3664,3669,3674,3679,3684,3689,3694,3699],{"__ignoreMap":75},[79,3445,3446],{"class":81,"line":82},[79,3447,3448],{"class":85},"-- Method 1: io.popen (cross-platform but shell-dependent)\n",[79,3450,3451,3453,3455,3458],{"class":81,"line":89},[79,3452,93],{"class":92},[79,3454,659],{"class":92},[79,3456,3457],{"class":371}," list_dir",[79,3459,3460],{"class":96},"(dir)\n",[79,3462,3463,3465,3467,3469,3471,3473,3475,3477,3480,3482,3485,3487,3490,3492,3495,3497,3500,3503,3506,3508,3510,3513,3515,3517],{"class":81,"line":123},[79,3464,670],{"class":92},[79,3466,3182],{"class":96},[79,3468,100],{"class":92},[79,3470,306],{"class":103},[79,3472,107],{"class":96},[79,3474,33],{"class":103},[79,3476,107],{"class":96},[79,3478,3479],{"class":110},"'ls -1 \"' ",[79,3481,149],{"class":92},[79,3483,3484],{"class":371}," dir",[79,3486,375],{"class":96},[79,3488,3489],{"class":103},"gsub",[79,3491,107],{"class":96},[79,3493,3494],{"class":110},"'\"'",[79,3496,114],{"class":96},[79,3498,3499],{"class":110},"'",[79,3501,3502],{"class":103},"\\\\",[79,3504,3505],{"class":110},"\"'",[79,3507,800],{"class":96},[79,3509,149],{"class":92},[79,3511,3512],{"class":110}," '\"'",[79,3514,114],{"class":96},[79,3516,117],{"class":110},[79,3518,321],{"class":96},[79,3520,3521,3523,3526,3528],{"class":81,"line":138},[79,3522,670],{"class":92},[79,3524,3525],{"class":96}," files ",[79,3527,100],{"class":92},[79,3529,2195],{"class":96},[79,3531,3532,3534,3536,3538,3540,3542,3544,3546],{"class":81,"line":167},[79,3533,692],{"class":92},[79,3535,695],{"class":96},[79,3537,698],{"class":92},[79,3539,3213],{"class":371},[79,3541,375],{"class":96},[79,3543,705],{"class":103},[79,3545,708],{"class":96},[79,3547,903],{"class":92},[79,3549,3550,3553,3555,3558,3560,3562,3565,3567],{"class":81,"line":173},[79,3551,3552],{"class":96},"    files[",[79,3554,2128],{"class":92},[79,3556,3557],{"class":96},"files ",[79,3559,812],{"class":92},[79,3561,815],{"class":103},[79,3563,3564],{"class":96},"] ",[79,3566,100],{"class":92},[79,3568,3569],{"class":96}," line\n",[79,3571,3572],{"class":81,"line":180},[79,3573,727],{"class":92},[79,3575,3576,3579,3581,3583],{"class":81,"line":186},[79,3577,3578],{"class":371},"  pipe",[79,3580,375],{"class":96},[79,3582,618],{"class":103},[79,3584,621],{"class":96},[79,3586,3587,3589],{"class":81,"line":192},[79,3588,1401],{"class":92},[79,3590,3591],{"class":96}," files\n",[79,3593,3594],{"class":81,"line":198},[79,3595,170],{"class":92},[79,3597,3598],{"class":81,"line":204},[79,3599,177],{"emptyLinePlaceholder":176},[79,3601,3602],{"class":81,"line":210},[79,3603,3604],{"class":85},"-- Method 2: luarocks install luafilesystem (lfs) — portable, no shell\n",[79,3606,3607],{"class":81,"line":216},[79,3608,3609],{"class":85},"-- local lfs = require(\"lfs\")\n",[79,3611,3612],{"class":81,"line":222},[79,3613,3614],{"class":85},"-- for entry in lfs.dir(\".\") do\n",[79,3616,3617],{"class":81,"line":228},[79,3618,3619],{"class":85},"--   local attr = lfs.attributes(entry)  -- {mode = \"file\"\u002F\"directory\", size = N, ...}\n",[79,3621,3622],{"class":81,"line":234},[79,3623,3624],{"class":85},"--   print(entry, attr.mode, attr.size)\n",[79,3626,3627],{"class":81,"line":240},[79,3628,3629],{"class":85},"-- end\n",[79,3631,3632],{"class":81,"line":245},[79,3633,177],{"emptyLinePlaceholder":176},[79,3635,3636],{"class":81,"line":251},[79,3637,3638],{"class":85},"-- Method 3: Recursive walk (with lfs)\n",[79,3640,3641],{"class":81,"line":257},[79,3642,3643],{"class":85},"-- local function walk(dir, callback)\n",[79,3645,3646],{"class":81,"line":263},[79,3647,3648],{"class":85},"--   for entry in lfs.dir(dir) do\n",[79,3650,3651],{"class":81,"line":269},[79,3652,3653],{"class":85},"--     if entry ~= \".\" and entry ~= \"..\" then\n",[79,3655,3656],{"class":81,"line":275},[79,3657,3658],{"class":85},"--       local path = dir .. \"\u002F\" .. entry\n",[79,3660,3661],{"class":81,"line":281},[79,3662,3663],{"class":85},"--       local attr = lfs.attributes(path)\n",[79,3665,3666],{"class":81,"line":916},[79,3667,3668],{"class":85},"--       if attr.mode == \"directory\" then\n",[79,3670,3671],{"class":81,"line":2093},[79,3672,3673],{"class":85},"--         walk(path, callback)\n",[79,3675,3676],{"class":81,"line":2104},[79,3677,3678],{"class":85},"--       else\n",[79,3680,3681],{"class":81,"line":2109},[79,3682,3683],{"class":85},"--         callback(path, attr)\n",[79,3685,3686],{"class":81,"line":2115},[79,3687,3688],{"class":85},"--       end\n",[79,3690,3691],{"class":81,"line":2136},[79,3692,3693],{"class":85},"--     end\n",[79,3695,3696],{"class":81,"line":2159},[79,3697,3698],{"class":85},"--   end\n",[79,3700,3701],{"class":81,"line":2165},[79,3702,3629],{"class":85},[60,3704,3706],{"id":3705},"production-csv-writer-streaming-quoted","Production: CSV Writer (streaming, quoted)",[67,3708,3709,4068,4072],{"language":69},[71,3710,3712],{"className":73,"code":3711,"language":69,"meta":75,"style":75},"local function csv_escape(field)\n  if type(field) == \"number\" then return tostring(field) end\n  field = tostring(field)\n  if field:match('[,\"\"\\r\\n]') then\n    return '\"' .. field:gsub('\"', '\"\"') .. '\"'  -- quote and escape internal quotes\n  end\n  return field\nend\n\nlocal function write_csv(path, rows)\n  local f \u003Cclose> = assert(io.open(path, \"w\"))\n  for _, row in ipairs(rows) do\n    local fields = {}\n    for _, v in ipairs(row) do\n      fields[#fields + 1] = csv_escape(v)\n    end\n    f:write(table.concat(fields, \",\"), \"\\n\")\n  end\nend\n\nwrite_csv(\"users.csv\", {\n  {\"name\", \"age\", \"note\"},\n  {\"Alice\", 30, 'has a \"quote\"'},\n  {\"Bob\", 25, \"no issues\"},\n})\n",[19,3713,3714,3726,3752,3763,3789,3823,3827,3834,3838,3842,3854,3876,3893,3905,3922,3945,3950,3979,3983,3987,3991,4004,4025,4044,4063],{"__ignoreMap":75},[79,3715,3716,3718,3720,3723],{"class":81,"line":82},[79,3717,93],{"class":92},[79,3719,659],{"class":92},[79,3721,3722],{"class":371}," csv_escape",[79,3724,3725],{"class":96},"(field)\n",[79,3727,3728,3730,3733,3736,3739,3742,3744,3746,3748,3750],{"class":81,"line":89},[79,3729,784],{"class":92},[79,3731,3732],{"class":103}," type",[79,3734,3735],{"class":96},"(field) ",[79,3737,3738],{"class":92},"==",[79,3740,3741],{"class":110}," \"number\" ",[79,3743,803],{"class":92},[79,3745,1271],{"class":92},[79,3747,1233],{"class":103},[79,3749,3735],{"class":96},[79,3751,170],{"class":92},[79,3753,3754,3757,3759,3761],{"class":81,"line":123},[79,3755,3756],{"class":96},"  field ",[79,3758,100],{"class":92},[79,3760,1233],{"class":103},[79,3762,3725],{"class":96},[79,3764,3765,3767,3770,3772,3774,3776,3779,3782,3785,3787],{"class":81,"line":138},[79,3766,784],{"class":92},[79,3768,3769],{"class":371}," field",[79,3771,375],{"class":96},[79,3773,792],{"class":103},[79,3775,107],{"class":96},[79,3777,3778],{"class":110},"'[,\"\"",[79,3780,3781],{"class":103},"\\r\\n",[79,3783,3784],{"class":110},"]'",[79,3786,800],{"class":96},[79,3788,135],{"class":92},[79,3790,3791,3793,3796,3798,3800,3802,3804,3806,3808,3810,3813,3815,3817,3820],{"class":81,"line":167},[79,3792,1376],{"class":92},[79,3794,3795],{"class":110}," '\"' ",[79,3797,149],{"class":92},[79,3799,3769],{"class":371},[79,3801,375],{"class":96},[79,3803,3489],{"class":103},[79,3805,107],{"class":96},[79,3807,3494],{"class":110},[79,3809,114],{"class":96},[79,3811,3812],{"class":110},"'\"\"'",[79,3814,800],{"class":96},[79,3816,149],{"class":92},[79,3818,3819],{"class":110}," '\"'  ",[79,3821,3822],{"class":85},"-- quote and escape internal quotes\n",[79,3824,3825],{"class":81,"line":173},[79,3826,727],{"class":92},[79,3828,3829,3831],{"class":81,"line":180},[79,3830,1401],{"class":92},[79,3832,3833],{"class":96}," field\n",[79,3835,3836],{"class":81,"line":186},[79,3837,170],{"class":92},[79,3839,3840],{"class":81,"line":192},[79,3841,177],{"emptyLinePlaceholder":176},[79,3843,3844,3846,3848,3851],{"class":81,"line":198},[79,3845,93],{"class":92},[79,3847,659],{"class":92},[79,3849,3850],{"class":371}," write_csv",[79,3852,3853],{"class":96},"(path, rows)\n",[79,3855,3856,3858,3860,3862,3864,3866,3868,3870,3872,3874],{"class":81,"line":204},[79,3857,670],{"class":92},[79,3859,132],{"class":96},[79,3861,42],{"class":92},[79,3863,869],{"class":92},[79,3865,306],{"class":103},[79,3867,107],{"class":96},[79,3869,21],{"class":103},[79,3871,683],{"class":96},[79,3873,953],{"class":110},[79,3875,321],{"class":96},[79,3877,3878,3880,3883,3885,3888,3891],{"class":81,"line":210},[79,3879,692],{"class":92},[79,3881,3882],{"class":96}," _, row ",[79,3884,698],{"class":92},[79,3886,3887],{"class":103}," ipairs",[79,3889,3890],{"class":96},"(rows) ",[79,3892,903],{"class":92},[79,3894,3895,3898,3901,3903],{"class":81,"line":216},[79,3896,3897],{"class":92},"    local",[79,3899,3900],{"class":96}," fields ",[79,3902,100],{"class":92},[79,3904,2195],{"class":96},[79,3906,3907,3910,3913,3915,3917,3920],{"class":81,"line":222},[79,3908,3909],{"class":92},"    for",[79,3911,3912],{"class":96}," _, v ",[79,3914,698],{"class":92},[79,3916,3887],{"class":103},[79,3918,3919],{"class":96},"(row) ",[79,3921,903],{"class":92},[79,3923,3924,3927,3929,3932,3934,3936,3938,3940,3942],{"class":81,"line":228},[79,3925,3926],{"class":96},"      fields[",[79,3928,2128],{"class":92},[79,3930,3931],{"class":96},"fields ",[79,3933,812],{"class":92},[79,3935,815],{"class":103},[79,3937,3564],{"class":96},[79,3939,100],{"class":92},[79,3941,3722],{"class":103},[79,3943,3944],{"class":96},"(v)\n",[79,3946,3947],{"class":81,"line":234},[79,3948,3949],{"class":92},"    end\n",[79,3951,3952,3954,3956,3958,3960,3963,3966,3969,3971,3973,3975,3977],{"class":81,"line":240},[79,3953,1302],{"class":371},[79,3955,375],{"class":96},[79,3957,973],{"class":103},[79,3959,107],{"class":96},[79,3961,3962],{"class":103},"table.concat",[79,3964,3965],{"class":96},"(fields, ",[79,3967,3968],{"class":110},"\",\"",[79,3970,1159],{"class":96},[79,3972,993],{"class":110},[79,3974,996],{"class":103},[79,3976,993],{"class":110},[79,3978,120],{"class":96},[79,3980,3981],{"class":81,"line":245},[79,3982,727],{"class":92},[79,3984,3985],{"class":81,"line":251},[79,3986,170],{"class":92},[79,3988,3989],{"class":81,"line":257},[79,3990,177],{"emptyLinePlaceholder":176},[79,3992,3993,3996,3998,4001],{"class":81,"line":263},[79,3994,3995],{"class":103},"write_csv",[79,3997,107],{"class":96},[79,3999,4000],{"class":110},"\"users.csv\"",[79,4002,4003],{"class":96},", {\n",[79,4005,4006,4009,4012,4014,4017,4019,4022],{"class":81,"line":269},[79,4007,4008],{"class":96},"  {",[79,4010,4011],{"class":110},"\"name\"",[79,4013,114],{"class":96},[79,4015,4016],{"class":110},"\"age\"",[79,4018,114],{"class":96},[79,4020,4021],{"class":110},"\"note\"",[79,4023,4024],{"class":96},"},\n",[79,4026,4027,4029,4032,4034,4037,4039,4042],{"class":81,"line":275},[79,4028,4008],{"class":96},[79,4030,4031],{"class":110},"\"Alice\"",[79,4033,114],{"class":96},[79,4035,4036],{"class":103},"30",[79,4038,114],{"class":96},[79,4040,4041],{"class":110},"'has a \"quote\"'",[79,4043,4024],{"class":96},[79,4045,4046,4048,4051,4053,4056,4058,4061],{"class":81,"line":281},[79,4047,4008],{"class":96},[79,4049,4050],{"class":110},"\"Bob\"",[79,4052,114],{"class":96},[79,4054,4055],{"class":103},"25",[79,4057,114],{"class":96},[79,4059,4060],{"class":110},"\"no issues\"",[79,4062,4024],{"class":96},[79,4064,4065],{"class":81,"line":916},[79,4066,4067],{"class":96},"})\n",[60,4069,4071],{"id":4070},"production-json-like-config-persistence","Production: JSON-like Config Persistence",[67,4073,4074,4853,4857,4867],{"language":69},[71,4075,4077],{"className":73,"code":4076,"language":69,"meta":75,"style":75},"-- Without cjson: a minimal Lua table serializer (for simple configs)\nlocal function serialize(val, indent, seen)\n  indent = indent or \"\"\n  seen = seen or {}\n  local t = type(val)\n  if t == \"nil\" then return \"nil\"\n  elseif t == \"boolean\" then return tostring(val)\n  elseif t == \"number\" then return tostring(val)\n  elseif t == \"string\" then return string.format(\"%q\", val)  -- %q: Lua-quoted string\n  elseif t == \"table\" then\n    if seen[val] then error(\"circular reference\", 2) end\n    seen[val] = true\n    local parts = {}\n    local has_array = false\n    local has_hash = false\n    for k, v in pairs(val) do\n      local key_str\n      if type(k) == \"number\" then\n        has_array = true\n        key_str = \"[\" .. k .. \"]\"\n      else\n        has_hash = true\n        key_str = string.format(\"[%q]\", k)\n      end\n      parts[#parts + 1] = indent .. \"  \" .. key_str .. \" = \" .. serialize(v, indent .. \"  \", seen)\n    end\n    if has_array and has_hash then\n      return \"{\\n\" .. table.concat(parts, \",\\n\") .. \"\\n\" .. indent .. \"}\"\n    elseif has_array then\n      -- pure array\n      local items = {}\n      for i, v in ipairs(val) do\n        items[i] = serialize(v, indent, seen)\n      end\n      return \"{\" .. table.concat(items, \", \") .. \"}\"\n    else\n      return \"{\\n\" .. table.concat(parts, \",\\n\") .. \"\\n\" .. indent .. \"}\"\n    end\n  end\n  error(\"cannot serialize \" .. t, 2)\nend\n\n-- Save:\nlocal config = {host = \"localhost\", port = 8080, features = {\"auth\", \"logging\"}}\nlocal f \u003Cclose> = assert(io.open(\"config.lua\", \"w\"))\nf:write(\"return \", serialize(config))\n-- config.lua contains: return {[\"host\"] = \"localhost\", [\"port\"] = 8080, {\"auth\", \"logging\"}}\n\n-- Load: it's a Lua file, so use loadfile (safe, since we control the format)\nlocal fn = assert(loadfile(\"config.lua\"))\nlocal loaded_config = fn()\nprint(loaded_config.host)  -- \"localhost\"\n",[19,4078,4079,4084,4096,4111,4125,4139,4157,4177,4195,4224,4237,4264,4273,4284,4296,4307,4324,4332,4348,4357,4377,4382,4391,4407,4412,4462,4466,4479,4527,4536,4541,4552,4568,4580,4584,4607,4612,4652,4656,4660,4678,4682,4686,4691,4734,4761,4782,4788,4793,4799,4822,4837],{"__ignoreMap":75},[79,4080,4081],{"class":81,"line":82},[79,4082,4083],{"class":85},"-- Without cjson: a minimal Lua table serializer (for simple configs)\n",[79,4085,4086,4088,4090,4093],{"class":81,"line":89},[79,4087,93],{"class":92},[79,4089,659],{"class":92},[79,4091,4092],{"class":371}," serialize",[79,4094,4095],{"class":96},"(val, indent, seen)\n",[79,4097,4098,4101,4103,4106,4108],{"class":81,"line":123},[79,4099,4100],{"class":96},"  indent ",[79,4102,100],{"class":92},[79,4104,4105],{"class":96}," indent ",[79,4107,155],{"class":92},[79,4109,4110],{"class":110}," \"\"\n",[79,4112,4113,4116,4118,4121,4123],{"class":81,"line":138},[79,4114,4115],{"class":96},"  seen ",[79,4117,100],{"class":92},[79,4119,4120],{"class":96}," seen ",[79,4122,155],{"class":92},[79,4124,2195],{"class":96},[79,4126,4127,4129,4132,4134,4136],{"class":81,"line":167},[79,4128,670],{"class":92},[79,4130,4131],{"class":96}," t ",[79,4133,100],{"class":92},[79,4135,3732],{"class":103},[79,4137,4138],{"class":96},"(val)\n",[79,4140,4141,4143,4145,4147,4150,4152,4154],{"class":81,"line":173},[79,4142,784],{"class":92},[79,4144,4131],{"class":96},[79,4146,3738],{"class":92},[79,4148,4149],{"class":110}," \"nil\" ",[79,4151,803],{"class":92},[79,4153,1271],{"class":92},[79,4155,4156],{"class":110}," \"nil\"\n",[79,4158,4159,4162,4164,4166,4169,4171,4173,4175],{"class":81,"line":180},[79,4160,4161],{"class":92},"  elseif",[79,4163,4131],{"class":96},[79,4165,3738],{"class":92},[79,4167,4168],{"class":110}," \"boolean\" ",[79,4170,803],{"class":92},[79,4172,1271],{"class":92},[79,4174,1233],{"class":103},[79,4176,4138],{"class":96},[79,4178,4179,4181,4183,4185,4187,4189,4191,4193],{"class":81,"line":186},[79,4180,4161],{"class":92},[79,4182,4131],{"class":96},[79,4184,3738],{"class":92},[79,4186,3741],{"class":110},[79,4188,803],{"class":92},[79,4190,1271],{"class":92},[79,4192,1233],{"class":103},[79,4194,4138],{"class":96},[79,4196,4197,4199,4201,4203,4206,4208,4210,4213,4215,4218,4221],{"class":81,"line":192},[79,4198,4161],{"class":92},[79,4200,4131],{"class":96},[79,4202,3738],{"class":92},[79,4204,4205],{"class":110}," \"string\" ",[79,4207,803],{"class":92},[79,4209,1271],{"class":92},[79,4211,4212],{"class":103}," string.format",[79,4214,107],{"class":96},[79,4216,4217],{"class":110},"\"%q\"",[79,4219,4220],{"class":96},", val)  ",[79,4222,4223],{"class":85},"-- %q: Lua-quoted string\n",[79,4225,4226,4228,4230,4232,4235],{"class":81,"line":198},[79,4227,4161],{"class":92},[79,4229,4131],{"class":96},[79,4231,3738],{"class":92},[79,4233,4234],{"class":110}," \"table\" ",[79,4236,135],{"class":92},[79,4238,4239,4242,4245,4247,4250,4252,4255,4257,4260,4262],{"class":81,"line":204},[79,4240,4241],{"class":92},"    if",[79,4243,4244],{"class":96}," seen[val] ",[79,4246,803],{"class":92},[79,4248,4249],{"class":103}," error",[79,4251,107],{"class":96},[79,4253,4254],{"class":110},"\"circular reference\"",[79,4256,114],{"class":96},[79,4258,4259],{"class":103},"2",[79,4261,800],{"class":96},[79,4263,170],{"class":92},[79,4265,4266,4269,4271],{"class":81,"line":210},[79,4267,4268],{"class":96},"    seen[val] ",[79,4270,100],{"class":92},[79,4272,1404],{"class":103},[79,4274,4275,4277,4280,4282],{"class":81,"line":216},[79,4276,3897],{"class":92},[79,4278,4279],{"class":96}," parts ",[79,4281,100],{"class":92},[79,4283,2195],{"class":96},[79,4285,4286,4288,4291,4293],{"class":81,"line":222},[79,4287,3897],{"class":92},[79,4289,4290],{"class":96}," has_array ",[79,4292,100],{"class":92},[79,4294,4295],{"class":103}," false\n",[79,4297,4298,4300,4303,4305],{"class":81,"line":228},[79,4299,3897],{"class":92},[79,4301,4302],{"class":96}," has_hash ",[79,4304,100],{"class":92},[79,4306,4295],{"class":103},[79,4308,4309,4311,4314,4316,4319,4322],{"class":81,"line":234},[79,4310,3909],{"class":92},[79,4312,4313],{"class":96}," k, v ",[79,4315,698],{"class":92},[79,4317,4318],{"class":103}," pairs",[79,4320,4321],{"class":96},"(val) ",[79,4323,903],{"class":92},[79,4325,4326,4329],{"class":81,"line":240},[79,4327,4328],{"class":92},"      local",[79,4330,4331],{"class":96}," key_str\n",[79,4333,4334,4337,4339,4342,4344,4346],{"class":81,"line":245},[79,4335,4336],{"class":92},"      if",[79,4338,3732],{"class":103},[79,4340,4341],{"class":96},"(k) ",[79,4343,3738],{"class":92},[79,4345,3741],{"class":110},[79,4347,135],{"class":92},[79,4349,4350,4353,4355],{"class":81,"line":251},[79,4351,4352],{"class":96},"        has_array ",[79,4354,100],{"class":92},[79,4356,1404],{"class":103},[79,4358,4359,4362,4364,4367,4369,4372,4374],{"class":81,"line":257},[79,4360,4361],{"class":96},"        key_str ",[79,4363,100],{"class":92},[79,4365,4366],{"class":110}," \"[\" ",[79,4368,149],{"class":92},[79,4370,4371],{"class":96}," k ",[79,4373,149],{"class":92},[79,4375,4376],{"class":110}," \"]\"\n",[79,4378,4379],{"class":81,"line":263},[79,4380,4381],{"class":92},"      else\n",[79,4383,4384,4387,4389],{"class":81,"line":269},[79,4385,4386],{"class":96},"        has_hash ",[79,4388,100],{"class":92},[79,4390,1404],{"class":103},[79,4392,4393,4395,4397,4399,4401,4404],{"class":81,"line":275},[79,4394,4361],{"class":96},[79,4396,100],{"class":92},[79,4398,4212],{"class":103},[79,4400,107],{"class":96},[79,4402,4403],{"class":110},"\"[%q]\"",[79,4405,4406],{"class":96},", k)\n",[79,4408,4409],{"class":81,"line":281},[79,4410,4411],{"class":92},"      end\n",[79,4413,4414,4417,4419,4422,4424,4426,4428,4430,4432,4434,4437,4439,4442,4444,4447,4449,4451,4454,4456,4459],{"class":81,"line":916},[79,4415,4416],{"class":96},"      parts[",[79,4418,2128],{"class":92},[79,4420,4421],{"class":96},"parts ",[79,4423,812],{"class":92},[79,4425,815],{"class":103},[79,4427,3564],{"class":96},[79,4429,100],{"class":92},[79,4431,4105],{"class":96},[79,4433,149],{"class":92},[79,4435,4436],{"class":110}," \"  \" ",[79,4438,149],{"class":92},[79,4440,4441],{"class":96}," key_str ",[79,4443,149],{"class":92},[79,4445,4446],{"class":110}," \" = \" ",[79,4448,149],{"class":92},[79,4450,4092],{"class":103},[79,4452,4453],{"class":96},"(v, indent ",[79,4455,149],{"class":92},[79,4457,4458],{"class":110}," \"  \"",[79,4460,4461],{"class":96},", seen)\n",[79,4463,4464],{"class":81,"line":2093},[79,4465,3949],{"class":92},[79,4467,4468,4470,4472,4475,4477],{"class":81,"line":2104},[79,4469,4241],{"class":92},[79,4471,4290],{"class":96},[79,4473,4474],{"class":92},"and",[79,4476,4302],{"class":96},[79,4478,135],{"class":92},[79,4480,4481,4484,4487,4489,4492,4494,4497,4500,4503,4505,4507,4509,4511,4514,4516,4518,4520,4522,4524],{"class":81,"line":2109},[79,4482,4483],{"class":92},"      return",[79,4485,4486],{"class":110}," \"{",[79,4488,996],{"class":103},[79,4490,4491],{"class":110},"\" ",[79,4493,149],{"class":92},[79,4495,4496],{"class":103}," table.concat",[79,4498,4499],{"class":96},"(parts, ",[79,4501,4502],{"class":110},"\",",[79,4504,996],{"class":103},[79,4506,993],{"class":110},[79,4508,800],{"class":96},[79,4510,149],{"class":92},[79,4512,4513],{"class":110}," \"",[79,4515,996],{"class":103},[79,4517,4491],{"class":110},[79,4519,149],{"class":92},[79,4521,4105],{"class":96},[79,4523,149],{"class":92},[79,4525,4526],{"class":110}," \"}\"\n",[79,4528,4529,4532,4534],{"class":81,"line":2115},[79,4530,4531],{"class":92},"    elseif",[79,4533,4290],{"class":96},[79,4535,135],{"class":92},[79,4537,4538],{"class":81,"line":2136},[79,4539,4540],{"class":85},"      -- pure array\n",[79,4542,4543,4545,4548,4550],{"class":81,"line":2159},[79,4544,4328],{"class":92},[79,4546,4547],{"class":96}," items ",[79,4549,100],{"class":92},[79,4551,2195],{"class":96},[79,4553,4554,4557,4560,4562,4564,4566],{"class":81,"line":2165},[79,4555,4556],{"class":92},"      for",[79,4558,4559],{"class":96}," i, v ",[79,4561,698],{"class":92},[79,4563,3887],{"class":103},[79,4565,4321],{"class":96},[79,4567,903],{"class":92},[79,4569,4570,4573,4575,4577],{"class":81,"line":2671},[79,4571,4572],{"class":96},"        items[i] ",[79,4574,100],{"class":92},[79,4576,4092],{"class":103},[79,4578,4579],{"class":96},"(v, indent, seen)\n",[79,4581,4582],{"class":81,"line":2685},[79,4583,4411],{"class":92},[79,4585,4586,4588,4591,4593,4595,4598,4601,4603,4605],{"class":81,"line":2712},[79,4587,4483],{"class":92},[79,4589,4590],{"class":110}," \"{\" ",[79,4592,149],{"class":92},[79,4594,4496],{"class":103},[79,4596,4597],{"class":96},"(items, ",[79,4599,4600],{"class":110},"\", \"",[79,4602,800],{"class":96},[79,4604,149],{"class":92},[79,4606,4526],{"class":110},[79,4608,4609],{"class":81,"line":2717},[79,4610,4611],{"class":92},"    else\n",[79,4613,4614,4616,4618,4620,4622,4624,4626,4628,4630,4632,4634,4636,4638,4640,4642,4644,4646,4648,4650],{"class":81,"line":2722},[79,4615,4483],{"class":92},[79,4617,4486],{"class":110},[79,4619,996],{"class":103},[79,4621,4491],{"class":110},[79,4623,149],{"class":92},[79,4625,4496],{"class":103},[79,4627,4499],{"class":96},[79,4629,4502],{"class":110},[79,4631,996],{"class":103},[79,4633,993],{"class":110},[79,4635,800],{"class":96},[79,4637,149],{"class":92},[79,4639,4513],{"class":110},[79,4641,996],{"class":103},[79,4643,4491],{"class":110},[79,4645,149],{"class":92},[79,4647,4105],{"class":96},[79,4649,149],{"class":92},[79,4651,4526],{"class":110},[79,4653,4654],{"class":81,"line":2728},[79,4655,3949],{"class":92},[79,4657,4658],{"class":81,"line":2740},[79,4659,727],{"class":92},[79,4661,4662,4664,4666,4669,4671,4674,4676],{"class":81,"line":2757},[79,4663,141],{"class":103},[79,4665,107],{"class":96},[79,4667,4668],{"class":110},"\"cannot serialize \" ",[79,4670,149],{"class":92},[79,4672,4673],{"class":96}," t, ",[79,4675,4259],{"class":103},[79,4677,120],{"class":96},[79,4679,4680],{"class":81,"line":2783},[79,4681,170],{"class":92},[79,4683,4684],{"class":81,"line":2805},[79,4685,177],{"emptyLinePlaceholder":176},[79,4687,4688],{"class":81,"line":2827},[79,4689,4690],{"class":85},"-- Save:\n",[79,4692,4693,4695,4698,4700,4703,4705,4708,4711,4713,4716,4719,4721,4723,4726,4728,4731],{"class":81,"line":2849},[79,4694,93],{"class":92},[79,4696,4697],{"class":96}," config ",[79,4699,100],{"class":92},[79,4701,4702],{"class":96}," {host ",[79,4704,100],{"class":92},[79,4706,4707],{"class":110}," \"localhost\"",[79,4709,4710],{"class":96},", port ",[79,4712,100],{"class":92},[79,4714,4715],{"class":103}," 8080",[79,4717,4718],{"class":96},", features ",[79,4720,100],{"class":92},[79,4722,1798],{"class":96},[79,4724,4725],{"class":110},"\"auth\"",[79,4727,114],{"class":96},[79,4729,4730],{"class":110},"\"logging\"",[79,4732,4733],{"class":96},"}}\n",[79,4735,4736,4738,4740,4742,4744,4746,4748,4750,4752,4755,4757,4759],{"class":81,"line":2868},[79,4737,93],{"class":92},[79,4739,132],{"class":96},[79,4741,42],{"class":92},[79,4743,869],{"class":92},[79,4745,306],{"class":103},[79,4747,107],{"class":96},[79,4749,21],{"class":103},[79,4751,107],{"class":96},[79,4753,4754],{"class":110},"\"config.lua\"",[79,4756,114],{"class":96},[79,4758,953],{"class":110},[79,4760,321],{"class":96},[79,4762,4763,4765,4767,4769,4771,4774,4776,4779],{"class":81,"line":2896},[79,4764,394],{"class":371},[79,4766,375],{"class":96},[79,4768,973],{"class":103},[79,4770,107],{"class":96},[79,4772,4773],{"class":110},"\"return \"",[79,4775,114],{"class":96},[79,4777,4778],{"class":103},"serialize",[79,4780,4781],{"class":96},"(config))\n",[79,4783,4785],{"class":81,"line":4784},47,[79,4786,4787],{"class":85},"-- config.lua contains: return {[\"host\"] = \"localhost\", [\"port\"] = 8080, {\"auth\", \"logging\"}}\n",[79,4789,4791],{"class":81,"line":4790},48,[79,4792,177],{"emptyLinePlaceholder":176},[79,4794,4796],{"class":81,"line":4795},49,[79,4797,4798],{"class":85},"-- Load: it's a Lua file, so use loadfile (safe, since we control the format)\n",[79,4800,4802,4804,4807,4809,4811,4813,4816,4818,4820],{"class":81,"line":4801},50,[79,4803,93],{"class":92},[79,4805,4806],{"class":96}," fn ",[79,4808,100],{"class":92},[79,4810,306],{"class":103},[79,4812,107],{"class":96},[79,4814,4815],{"class":103},"loadfile",[79,4817,107],{"class":96},[79,4819,4754],{"class":110},[79,4821,321],{"class":96},[79,4823,4825,4827,4830,4832,4835],{"class":81,"line":4824},51,[79,4826,93],{"class":92},[79,4828,4829],{"class":96}," loaded_config ",[79,4831,100],{"class":92},[79,4833,4834],{"class":103}," fn",[79,4836,621],{"class":96},[79,4838,4840,4842,4845,4848,4850],{"class":81,"line":4839},52,[79,4841,830],{"class":103},[79,4843,4844],{"class":96},"(loaded_config.",[79,4846,4847],{"class":371},"host",[79,4849,433],{"class":96},[79,4851,4852],{"class":85},"-- \"localhost\"\n",[60,4854,4856],{"id":4855},"tips-tricks","💡 Tips & Tricks",[15,4858,4859,4866],{},[36,4860,4861,4862,4865],{},"Use ",[19,4863,4864],{},"f:lines()"," for any file >1MB",": Avoids loading the entire file into memory.",[67,4868,4869],{"language":69},[71,4870,4872],{"className":73,"code":4871,"language":69,"meta":75,"style":75},"-- BAD: loads entire file (10GB → 10GB RAM)\nlocal content = io.open(\"huge.log\", \"r\"):read(\"*a\")\n\n-- GOOD: streams one line at a time (constant memory)\nfor line in io.open(\"huge.log\", \"r\"):lines() do\n  process(line)\nend\n",[19,4873,4874,4879,4909,4913,4918,4944,4951],{"__ignoreMap":75},[79,4875,4876],{"class":81,"line":82},[79,4877,4878],{"class":85},"-- BAD: loads entire file (10GB → 10GB RAM)\n",[79,4880,4881,4883,4886,4888,4890,4892,4894,4896,4898,4901,4903,4905,4907],{"class":81,"line":89},[79,4882,93],{"class":92},[79,4884,4885],{"class":96}," content ",[79,4887,100],{"class":92},[79,4889,104],{"class":103},[79,4891,107],{"class":96},[79,4893,772],{"class":110},[79,4895,114],{"class":96},[79,4897,117],{"class":110},[79,4899,4900],{"class":96},"):",[79,4902,378],{"class":103},[79,4904,107],{"class":96},[79,4906,383],{"class":110},[79,4908,120],{"class":96},[79,4910,4911],{"class":81,"line":123},[79,4912,177],{"emptyLinePlaceholder":176},[79,4914,4915],{"class":81,"line":138},[79,4916,4917],{"class":85},"-- GOOD: streams one line at a time (constant memory)\n",[79,4919,4920,4922,4924,4926,4928,4930,4932,4934,4936,4938,4940,4942],{"class":81,"line":167},[79,4921,1854],{"class":92},[79,4923,695],{"class":96},[79,4925,698],{"class":92},[79,4927,104],{"class":103},[79,4929,107],{"class":96},[79,4931,772],{"class":110},[79,4933,114],{"class":96},[79,4935,117],{"class":110},[79,4937,4900],{"class":96},[79,4939,705],{"class":103},[79,4941,708],{"class":96},[79,4943,903],{"class":92},[79,4945,4946,4949],{"class":81,"line":173},[79,4947,4948],{"class":103},"  process",[79,4950,722],{"class":96},[79,4952,4953],{"class":81,"line":180},[79,4954,170],{"class":92},[15,4956,4957,4965,4966,4968,4969,4971],{},[36,4958,4959,4961,4962,4964],{},[19,4960,29],{}," is faster than ",[19,4963,830],{}," for raw output",": ",[19,4967,830],{}," adds a tab between args and newline at end; ",[19,4970,29],{}," writes exactly what you give it.",[15,4973,4974,4984,4985,4988],{},[36,4975,4976,4979,4980,4983],{},[19,4977,4978],{},"f:seek(\"end\")"," then ",[19,4981,4982],{},"f:seek()"," to get file size",": No ",[19,4986,4987],{},"stat"," needed.",[67,4990,4991],{"language":69},[71,4992,4994],{"className":73,"code":4993,"language":69,"meta":75,"style":75},"local f = io.open(\"file.bin\", \"rb\")\nf:seek(\"end\")\nlocal size = f:seek()\nf:seek(\"set\", 0)  -- rewind if you need to read\n",[19,4995,4996,5017,5031,5047],{"__ignoreMap":75},[79,4997,4998,5000,5002,5004,5006,5008,5011,5013,5015],{"class":81,"line":82},[79,4999,93],{"class":92},[79,5001,132],{"class":96},[79,5003,100],{"class":92},[79,5005,104],{"class":103},[79,5007,107],{"class":96},[79,5009,5010],{"class":110},"\"file.bin\"",[79,5012,114],{"class":96},[79,5014,1489],{"class":110},[79,5016,120],{"class":96},[79,5018,5019,5021,5023,5025,5027,5029],{"class":81,"line":89},[79,5020,394],{"class":371},[79,5022,375],{"class":96},[79,5024,399],{"class":103},[79,5026,107],{"class":96},[79,5028,1508],{"class":110},[79,5030,120],{"class":96},[79,5032,5033,5035,5037,5039,5041,5043,5045],{"class":81,"line":123},[79,5034,93],{"class":92},[79,5036,1521],{"class":96},[79,5038,100],{"class":92},[79,5040,372],{"class":371},[79,5042,375],{"class":96},[79,5044,399],{"class":103},[79,5046,621],{"class":96},[79,5048,5049,5051,5053,5055,5057,5059,5061,5063,5065],{"class":81,"line":138},[79,5050,394],{"class":371},[79,5052,375],{"class":96},[79,5054,399],{"class":103},[79,5056,107],{"class":96},[79,5058,404],{"class":110},[79,5060,114],{"class":96},[79,5062,1571],{"class":103},[79,5064,433],{"class":96},[79,5066,5067],{"class":85},"-- rewind if you need to read\n",[15,5069,5070,5076],{},[36,5071,5072,5075],{},[19,5073,5074],{},"string.format(\"%q\", s)"," for safe Lua-string serialization",": Produces a string that, when parsed by Lua, gives back the original. Handles quotes, newlines, embedded nulls.",[60,5078,5080],{"id":5079},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[15,5082,5083,5086,5087,5090,5091,58],{},[36,5084,5085],{},"File handles are NOT reliably garbage-collected",": Relying on GC to close files causes resource leaks (may hit OS file descriptor limit). Always ",[19,5088,5089],{},"f:close()"," or use ",[19,5092,42],{},[67,5094,5095],{"language":69},[71,5096,5098],{"className":73,"code":5097,"language":69,"meta":75,"style":75},"-- BAD: f leaks until GC (which may be much later, or never)\nlocal function read_all(path)\n  return io.open(path, \"r\"):read(\"*a\")  -- f never closed!\nend\n\n-- GOOD: explicit close\nlocal function read_all(path)\n  local f = assert(io.open(path, \"r\"))\n  local content = f:read(\"*a\")\n  f:close()\n  return content\nend\n\n-- BEST: \u003Cclose> (5.4+), handles errors automatically\nlocal function read_all(path)\n  local f \u003Cclose> = assert(io.open(path, \"r\"))\n  return f:read(\"*a\")\nend\n",[19,5099,5100,5105,5117,5140,5144,5148,5153,5163,5183,5203,5213,5220,5224,5228,5233,5243,5265,5281],{"__ignoreMap":75},[79,5101,5102],{"class":81,"line":82},[79,5103,5104],{"class":85},"-- BAD: f leaks until GC (which may be much later, or never)\n",[79,5106,5107,5109,5111,5114],{"class":81,"line":89},[79,5108,93],{"class":92},[79,5110,659],{"class":92},[79,5112,5113],{"class":371}," read_all",[79,5115,5116],{"class":96},"(path)\n",[79,5118,5119,5121,5123,5125,5127,5129,5131,5133,5135,5137],{"class":81,"line":123},[79,5120,1401],{"class":92},[79,5122,104],{"class":103},[79,5124,683],{"class":96},[79,5126,117],{"class":110},[79,5128,4900],{"class":96},[79,5130,378],{"class":103},[79,5132,107],{"class":96},[79,5134,383],{"class":110},[79,5136,433],{"class":96},[79,5138,5139],{"class":85},"-- f never closed!\n",[79,5141,5142],{"class":81,"line":138},[79,5143,170],{"class":92},[79,5145,5146],{"class":81,"line":167},[79,5147,177],{"emptyLinePlaceholder":176},[79,5149,5150],{"class":81,"line":173},[79,5151,5152],{"class":85},"-- GOOD: explicit close\n",[79,5154,5155,5157,5159,5161],{"class":81,"line":180},[79,5156,93],{"class":92},[79,5158,659],{"class":92},[79,5160,5113],{"class":371},[79,5162,5116],{"class":96},[79,5164,5165,5167,5169,5171,5173,5175,5177,5179,5181],{"class":81,"line":186},[79,5166,670],{"class":92},[79,5168,132],{"class":96},[79,5170,100],{"class":92},[79,5172,306],{"class":103},[79,5174,107],{"class":96},[79,5176,21],{"class":103},[79,5178,683],{"class":96},[79,5180,117],{"class":110},[79,5182,321],{"class":96},[79,5184,5185,5187,5189,5191,5193,5195,5197,5199,5201],{"class":81,"line":192},[79,5186,670],{"class":92},[79,5188,4885],{"class":96},[79,5190,100],{"class":92},[79,5192,372],{"class":371},[79,5194,375],{"class":96},[79,5196,378],{"class":103},[79,5198,107],{"class":96},[79,5200,383],{"class":110},[79,5202,120],{"class":96},[79,5204,5205,5207,5209,5211],{"class":81,"line":198},[79,5206,732],{"class":371},[79,5208,375],{"class":96},[79,5210,618],{"class":103},[79,5212,621],{"class":96},[79,5214,5215,5217],{"class":81,"line":204},[79,5216,1401],{"class":92},[79,5218,5219],{"class":96}," content\n",[79,5221,5222],{"class":81,"line":210},[79,5223,170],{"class":92},[79,5225,5226],{"class":81,"line":216},[79,5227,177],{"emptyLinePlaceholder":176},[79,5229,5230],{"class":81,"line":222},[79,5231,5232],{"class":85},"-- BEST: \u003Cclose> (5.4+), handles errors automatically\n",[79,5234,5235,5237,5239,5241],{"class":81,"line":228},[79,5236,93],{"class":92},[79,5238,659],{"class":92},[79,5240,5113],{"class":371},[79,5242,5116],{"class":96},[79,5244,5245,5247,5249,5251,5253,5255,5257,5259,5261,5263],{"class":81,"line":234},[79,5246,670],{"class":92},[79,5248,132],{"class":96},[79,5250,42],{"class":92},[79,5252,869],{"class":92},[79,5254,306],{"class":103},[79,5256,107],{"class":96},[79,5258,21],{"class":103},[79,5260,683],{"class":96},[79,5262,117],{"class":110},[79,5264,321],{"class":96},[79,5266,5267,5269,5271,5273,5275,5277,5279],{"class":81,"line":240},[79,5268,1401],{"class":92},[79,5270,372],{"class":371},[79,5272,375],{"class":96},[79,5274,378],{"class":103},[79,5276,107],{"class":96},[79,5278,383],{"class":110},[79,5280,120],{"class":96},[79,5282,5283],{"class":81,"line":245},[79,5284,170],{"class":92},[15,5286,5287,5293,5294,5296,5297,5299,5300,5302,5303,5299,5305,5307,5308,26,5310,5312],{},[36,5288,5289,5290,5292],{},"Text mode translates ",[19,5291,3781],{}," on Windows",": Reading in ",[19,5295,117],{}," mode converts ",[19,5298,3781],{}," to ",[19,5301,996],{},"; writing converts ",[19,5304,996],{},[19,5306,3781],{},". Use ",[19,5309,1489],{},[19,5311,1969],{}," for exact byte control.",[15,5314,5315,5320,5321,5324,5325,5328],{},[36,5316,5317,5319],{},[19,5318,33],{}," is shell-dependent and a security risk",": The command is passed to ",[19,5322,5323],{},"\u002Fbin\u002Fsh -c"," (POSIX) or ",[19,5326,5327],{},"cmd.exe"," (Windows). Never interpolate untrusted input.",[15,5330,5331,5341],{},[36,5332,5333,5336,5337,5340],{},[19,5334,5335],{},"f:read(\"*n\")"," returns ",[19,5338,5339],{},"nil"," if no number at position",": Doesn't error — silently returns nil. Check the return value.",[15,5343,5344,5353,5354,5356],{},[36,5345,5346,5336,5349,5352],{},[19,5347,5348],{},"f:read(0)",[19,5350,5351],{},"\"\""," (empty string), not nil",": Used to test for EOF without consuming. Returns ",[19,5355,5339],{}," only at EOF.",[60,5358,5360],{"id":5359},"spot-the-bug","🧠 Spot the Bug",[67,5362,5363,5541],{"language":69},[71,5364,5366],{"className":73,"code":5365,"language":69,"meta":75,"style":75},"local function count_lines(path)\n  local f = io.open(path, \"r\")\n  local count = 0\n  for line in f:lines() do\n    count = count + 1\n  end\n  return count\nend\n\nlocal function read_first_line(path)\n  local f = io.open(path, \"r\")\n  return f:read(\"*l\")\nend\n\n-- Call sequence:\nprint(count_lines(\"data.txt\"))\nprint(read_first_line(\"data.txt\"))\n",[19,5367,5368,5379,5395,5405,5423,5436,5440,5447,5451,5455,5466,5482,5498,5502,5506,5511,5526],{"__ignoreMap":75},[79,5369,5370,5372,5374,5377],{"class":81,"line":82},[79,5371,93],{"class":92},[79,5373,659],{"class":92},[79,5375,5376],{"class":371}," count_lines",[79,5378,5116],{"class":96},[79,5380,5381,5383,5385,5387,5389,5391,5393],{"class":81,"line":89},[79,5382,670],{"class":92},[79,5384,132],{"class":96},[79,5386,100],{"class":92},[79,5388,104],{"class":103},[79,5390,683],{"class":96},[79,5392,117],{"class":110},[79,5394,120],{"class":96},[79,5396,5397,5399,5401,5403],{"class":81,"line":123},[79,5398,670],{"class":92},[79,5400,757],{"class":96},[79,5402,100],{"class":92},[79,5404,762],{"class":103},[79,5406,5407,5409,5411,5413,5415,5417,5419,5421],{"class":81,"line":138},[79,5408,692],{"class":92},[79,5410,695],{"class":96},[79,5412,698],{"class":92},[79,5414,372],{"class":371},[79,5416,375],{"class":96},[79,5418,705],{"class":103},[79,5420,708],{"class":96},[79,5422,903],{"class":92},[79,5424,5425,5428,5430,5432,5434],{"class":81,"line":167},[79,5426,5427],{"class":96},"    count ",[79,5429,100],{"class":92},[79,5431,757],{"class":96},[79,5433,812],{"class":92},[79,5435,2330],{"class":103},[79,5437,5438],{"class":81,"line":173},[79,5439,727],{"class":92},[79,5441,5442,5444],{"class":81,"line":180},[79,5443,1401],{"class":92},[79,5445,5446],{"class":96}," count\n",[79,5448,5449],{"class":81,"line":186},[79,5450,170],{"class":92},[79,5452,5453],{"class":81,"line":192},[79,5454,177],{"emptyLinePlaceholder":176},[79,5456,5457,5459,5461,5464],{"class":81,"line":198},[79,5458,93],{"class":92},[79,5460,659],{"class":92},[79,5462,5463],{"class":371}," read_first_line",[79,5465,5116],{"class":96},[79,5467,5468,5470,5472,5474,5476,5478,5480],{"class":81,"line":204},[79,5469,670],{"class":92},[79,5471,132],{"class":96},[79,5473,100],{"class":92},[79,5475,104],{"class":103},[79,5477,683],{"class":96},[79,5479,117],{"class":110},[79,5481,120],{"class":96},[79,5483,5484,5486,5488,5490,5492,5494,5496],{"class":81,"line":210},[79,5485,1401],{"class":92},[79,5487,372],{"class":371},[79,5489,375],{"class":96},[79,5491,378],{"class":103},[79,5493,107],{"class":96},[79,5495,430],{"class":110},[79,5497,120],{"class":96},[79,5499,5500],{"class":81,"line":216},[79,5501,170],{"class":92},[79,5503,5504],{"class":81,"line":222},[79,5505,177],{"emptyLinePlaceholder":176},[79,5507,5508],{"class":81,"line":228},[79,5509,5510],{"class":85},"-- Call sequence:\n",[79,5512,5513,5515,5517,5520,5522,5524],{"class":81,"line":234},[79,5514,830],{"class":103},[79,5516,107],{"class":96},[79,5518,5519],{"class":103},"count_lines",[79,5521,107],{"class":96},[79,5523,111],{"class":110},[79,5525,321],{"class":96},[79,5527,5528,5530,5532,5535,5537,5539],{"class":81,"line":240},[79,5529,830],{"class":103},[79,5531,107],{"class":96},[79,5533,5534],{"class":103},"read_first_line",[79,5536,107],{"class":96},[79,5538,111],{"class":110},[79,5540,321],{"class":96},[5542,5543,5544,5548,5551,5581,5584],"details",{},[5545,5546,5547],"summary",{},"Answer",[15,5549,5550],{},"Two bugs:",[5552,5553,5554,5561],"ol",{},[5555,5556,5557,5560],"li",{},[36,5558,5559],{},"File handles never closed"," — both functions leak file descriptors. Called in a loop, this will exhaust the OS file descriptor limit (usually ~256 or ~1024).",[5555,5562,5563,5568,5569,5571,5572,5574,5575,3432,5577,5580],{},[36,5564,5565,5566],{},"No error handling on ",[19,5567,21],{}," — if the file doesn't exist, ",[19,5570,394],{}," is ",[19,5573,5339],{},", and ",[19,5576,4864],{},[19,5578,5579],{},"f:read(\"*l\")"," errors with \"attempt to index a nil value\" — misleading.",[15,5582,5583],{},"Fix:",[71,5585,5587],{"className":73,"code":5586,"language":69,"meta":75,"style":75},"local function count_lines(path)\n  local f = assert(io.open(path, \"r\"))  -- error with clear message if missing\n  local count = 0\n  for line in f:lines() do count = count + 1 end\n  f:close()  -- ALWAYS close\n  return count\nend\n\n-- Or with \u003Cclose> (5.4+):\nlocal function read_first_line(path)\n  local f \u003Cclose> = assert(io.open(path, \"r\"))\n  return f:read(\"*l\")\nend\n",[19,5588,5589,5599,5622,5632,5662,5675,5681,5685,5689,5694,5704,5726,5742],{"__ignoreMap":75},[79,5590,5591,5593,5595,5597],{"class":81,"line":82},[79,5592,93],{"class":92},[79,5594,659],{"class":92},[79,5596,5376],{"class":371},[79,5598,5116],{"class":96},[79,5600,5601,5603,5605,5607,5609,5611,5613,5615,5617,5619],{"class":81,"line":89},[79,5602,670],{"class":92},[79,5604,132],{"class":96},[79,5606,100],{"class":92},[79,5608,306],{"class":103},[79,5610,107],{"class":96},[79,5612,21],{"class":103},[79,5614,683],{"class":96},[79,5616,117],{"class":110},[79,5618,161],{"class":96},[79,5620,5621],{"class":85},"-- error with clear message if missing\n",[79,5623,5624,5626,5628,5630],{"class":81,"line":123},[79,5625,670],{"class":92},[79,5627,757],{"class":96},[79,5629,100],{"class":92},[79,5631,762],{"class":103},[79,5633,5634,5636,5638,5640,5642,5644,5646,5648,5650,5652,5654,5656,5658,5660],{"class":81,"line":138},[79,5635,692],{"class":92},[79,5637,695],{"class":96},[79,5639,698],{"class":92},[79,5641,372],{"class":371},[79,5643,375],{"class":96},[79,5645,705],{"class":103},[79,5647,708],{"class":96},[79,5649,711],{"class":92},[79,5651,757],{"class":96},[79,5653,100],{"class":92},[79,5655,757],{"class":96},[79,5657,812],{"class":92},[79,5659,815],{"class":103},[79,5661,818],{"class":92},[79,5663,5664,5666,5668,5670,5672],{"class":81,"line":167},[79,5665,732],{"class":371},[79,5667,375],{"class":96},[79,5669,618],{"class":103},[79,5671,739],{"class":96},[79,5673,5674],{"class":85},"-- ALWAYS close\n",[79,5676,5677,5679],{"class":81,"line":173},[79,5678,1401],{"class":92},[79,5680,5446],{"class":96},[79,5682,5683],{"class":81,"line":180},[79,5684,170],{"class":92},[79,5686,5687],{"class":81,"line":186},[79,5688,177],{"emptyLinePlaceholder":176},[79,5690,5691],{"class":81,"line":192},[79,5692,5693],{"class":85},"-- Or with \u003Cclose> (5.4+):\n",[79,5695,5696,5698,5700,5702],{"class":81,"line":198},[79,5697,93],{"class":92},[79,5699,659],{"class":92},[79,5701,5463],{"class":371},[79,5703,5116],{"class":96},[79,5705,5706,5708,5710,5712,5714,5716,5718,5720,5722,5724],{"class":81,"line":204},[79,5707,670],{"class":92},[79,5709,132],{"class":96},[79,5711,42],{"class":92},[79,5713,869],{"class":92},[79,5715,306],{"class":103},[79,5717,107],{"class":96},[79,5719,21],{"class":103},[79,5721,683],{"class":96},[79,5723,117],{"class":110},[79,5725,321],{"class":96},[79,5727,5728,5730,5732,5734,5736,5738,5740],{"class":81,"line":210},[79,5729,1401],{"class":92},[79,5731,372],{"class":371},[79,5733,375],{"class":96},[79,5735,378],{"class":103},[79,5737,107],{"class":96},[79,5739,430],{"class":110},[79,5741,120],{"class":96},[79,5743,5744],{"class":81,"line":216},[79,5745,170],{"class":92},[5747,5748,5749],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}",{"title":75,"searchDepth":89,"depth":89,"links":5751},[5752,5754,5755,5758,5759,5760],{"id":62,"depth":89,"text":5753},"File Handles & io.open",{"id":287,"depth":89,"text":288},{"id":921,"depth":89,"text":922,"children":5756},[5757],{"id":1181,"depth":123,"text":1182},{"id":1433,"depth":89,"text":1434},{"id":5079,"depth":89,"text":5080},{"id":5359,"depth":89,"text":5360},"[object Object]","md",{},"\u002Flua\u002F10-io-and-files",{"title":5,"description":5766},{"Deep-dive into Lua's I\u002FO model":5767},"io.open with all modes, file handles as objects, streaming with :lines() for memory-efficient processing, binary I\u002FO with string.byte\u002Fchar, io.popen for subprocesses, seek\u002Ftell for random access, and production patterns for atomic writes, CSV streaming, and config persistence.","lua\u002F10-io-and-files","bxw14DDzjR96JfeEew8fLrt0icO77QbduOy7zKieTco",1789924650531]