Zig std.mem.replace: คำนวณขนาดก่อนแทนข้อความโดยไม่เดา buffer
การแทนข้อความใน Zig ต้องรู้ว่าปลายทางใหญ่แค่ไหน เพราะ std.mem.replace เขียนลง buffer ที่ผู้เรียกเตรียมเอง ถ้าเดาขนาดจาก input อย่างเดียว replacement ที่ยาวขึ้นอาจทำให้พื้นที่ไม่พอ std.mem.replacementSize จึงคำนวณขนาดสุดท้ายจากจำนวน match ก่อนจองหน่วยความจำครับ
WHY: วัดชั้นก่อนซื้อกล่อง
เหมือนเปลี่ยนขวดน้ำเล็กทุกใบเป็นกระติกใหญ่ เราต้องนับจำนวนและวัดพื้นที่ใหม่ ไม่ใช่ใช้ลังเดิมแล้วหวังว่าจะพอดี วิธีสองขั้นนี้ทำให้ allocator รับขนาดตรงและเปิดจุดให้ปฏิเสธ output ที่โตเกิน policy ก่อนเขียนจริง
const std = @import("std");
fn replaceOwned(allocator: std.mem.Allocator, input: []const u8, needle: []const u8, replacement: []const u8, max: usize) ![]u8 {
const size = std.mem.replacementSize(u8, input, needle, replacement);
if (size > max) return error.OutputTooLarge;
const out = try allocator.alloc(u8, size);
errdefer allocator.free(out);
const count = std.mem.replace(u8, input, needle, replacement, out);
if (count == 0 and !std.mem.eql(u8, input, out)) return error.UnexpectedOutput;
return out;
}
test "replace uses the exact computed size" {
const allocator = std.testing.allocator;
const out = try replaceOwned(allocator, "ชา-ชา-กาแฟ", "ชา", "ชาไทย", 64);
defer allocator.free(out);
try std.testing.expectEqualStrings("ชาไทย-ชาไทย-กาแฟ", out);
try std.testing.expectError(error.OutputTooLarge, replaceOwned(allocator, "aa", "a", "ยาว", 3));
}
HOW: คำนวณ จอง เขียน และคืนเจ้าของให้ชัด
ฟังก์ชันตัวอย่างคืน owned slice ผู้เรียกจึงต้อง free ด้วย allocator เดียวกัน errdefer ป้องกัน memory leak ถ้าเกิด error หลังจอง ส่วนเพดาน max ต้องมาจากข้อจำกัดของงาน เช่นขนาดข้อความตอบกลับ ไม่ใช่ค่าที่ผู้โจมตีส่งมาควบคุมได้
needle ว่างเป็นกรณีพิเศษที่ควรปฏิเสธหรือกำหนด semantics ก่อนเรียก เพราะคำว่า “แทนช่องว่างทุกตำแหน่ง” ทำให้จำนวน insertion ไม่เป็น intuition เดียวกับ match ปกติ นอกจากนี้ API ทำงานกับ slice ของ element จึงไม่ได้รู้ boundary ของ UTF-8 ถ้าแทน byte pattern ที่ตัดกลางอักขระ ผลลัพธ์อาจไม่ใช่ข้อความ UTF-8 ที่ถูกต้อง
Recap
- ใช้
replacementSizeก่อนจองปลายทาง - ตั้งเพดาน output ก่อน allocation
- ระบุ ownership และ allocator ของค่าที่คืน
- กำหนดนโยบาย needle ว่างและ UTF-8 ให้ชัด
- ทดสอบ no-match, หลาย match, replacement สั้น/ยาว และเพดาน