นากสีมิ้นต์ต้นฉบับกำลังเทียบม้วนกระดาษสองแผ่นที่มีรอยสีอำพันต่างกันเพียงจุดเดียว

Java Files.mismatch: หาตำแหน่งต่างแรกก่อนเปิด diff ใหญ่

พี่โชว์Engineering

ก่อนสร้าง diff รายบรรทัด เรามักต้องตอบคำถามง่ายกว่าว่าไฟล์สองไฟล์เหมือนกันหรือไม่ และต่างครั้งแรกตรง byte ไหนครับ Files.mismatch คืน -1 เมื่อเนื้อหาเท่ากัน หรือคืนตำแหน่ง byte แรกที่ต่าง จึงใช้เป็น fast diagnostic gate ได้โดยไม่ต้องเขียน loop อ่าน buffer เอง

WHY: byte offset เป็นสัญญาที่เล็กและชัด

method นี้ไม่อธิบายว่าบรรทัดใดเปลี่ยนหรือความหมายต่างอย่างไร มันทำหน้าที่เหมือนไม้บรรทัดวางบนกระดาษสองแผ่นแล้วชี้จุดแรกที่หมึกไม่ตรง การแยก detection ออกจาก presentation ช่วยให้ path ที่เพียงตรวจ artifact ทำงานตรงไปตรงมา ส่วน UI ค่อยเปิด text diff เมื่อจำเป็น

ถ้าไฟล์หนึ่งเป็น prefix ของอีกไฟล์ ผลคือขนาดของไฟล์ที่สั้นกว่า เพราะ byte จนถึงจุดนั้นตรงกันหมดแต่ไฟล์หนึ่งจบก่อน

HOW: ทดสอบ equal, changed และ prefix

import java.nio.file.Files;
import java.nio.file.Path;

public class Main {
    public static void main(String[] args) throws Exception {
        Path dir = Files.createTempDirectory("mismatch-example");
        Path left = dir.resolve("left.bin");
        Path right = dir.resolve("right.bin");

        Files.write(left, new byte[] {10, 20, 30});
        Files.write(right, new byte[] {10, 20, 30});
        assert Files.mismatch(left, right) == -1L;

        Files.write(right, new byte[] {10, 99, 30});
        assert Files.mismatch(left, right) == 1L;

        Files.write(right, new byte[] {10, 20});
        assert Files.mismatch(left, right) == 2L;
    }
}

ตัวอย่างใช้ temp directory เพื่อไม่แตะไฟล์ผู้ใช้ ใน production ต้องจัดการ IOException และสิทธิ์อ่านตาม policy อย่าเอา path จาก request ไปเปิดโดยไม่ normalize และจำกัด root เพราะ path traversal เป็นปัญหาคนละชั้นกับการเปรียบเทียบ

ขอบเขตของคำตอบ

byte offset ไม่เท่ากับ character index สำหรับ UTF-8 หากต้องแสดงบรรทัดและคอลัมน์ ให้เปิด decoder ตั้งแต่ช่วงที่เหมาะสมหรือใช้ text diff library ที่เข้าใจ encoding ไม่ควร slice byte กลาง multibyte sequence แล้วแปลงตรง ๆ

ไฟล์อาจเปลี่ยนระหว่างการตรวจและการใช้งาน ซึ่งเป็น TOCTOU ถ้าผลต้องใช้เป็นหลักฐาน release ให้เปรียบเทียบ immutable artifact หรือเปิด handle/ใช้ digest ที่ผูกกับ artifact store ตาม threat model สำหรับไฟล์ใหญ่มากและ remote filesystem ต้องวัด I/O จริง

Recap

  • -1 หมายถึงเนื้อหาเท่ากัน ค่าอื่นคือ byte offset แรกที่ต่าง
  • prefix ต่างกันที่ขนาดของไฟล์สั้นกว่า
  • แปลง byte offset ก่อนแสดงตำแหน่งข้อความ
  • จำกัด path และจัดการ IOException ที่ boundary
  • ใช้ immutable artifact เมื่อผลมีผลต่อความปลอดภัยหรือ release

เพิ่ม mismatch เป็น cheap gate ก่อนเรียก diff ราคาแพง แล้วเก็บ metric ว่าช่วยตัดงานได้มากเพียงใดครับ