无标题
Hỗ trợ Lua int64
Hỗ trợ Lua int64
Lua hỗ trợ kiểu dữ liệu int64 lâu nay vẫn là một chủ đề thú vị đáng được bàn luận. Dù tôi đã chia sẻ trên Twitter và gửi thư đến danh sách thảo luận Lua mailing list, tôi vẫn muốn viết một bài blog chi tiết hơn để ghi lại những suy nghĩ này.
Lua về cơ bản chỉ hỗ trợ duy nhất kiểu số học (number), vốn dĩ là kiểu double theo mặc định. Tuy nhiên bạn hoàn toàn có thể chỉnh sửa tập tin cấu hình luaconf.h để chuyển kiểu number sang int64. Tuy nhiên, đánh đổi việc từ bỏ khả năng xử lý số thực (floating point) chỉ để lấy int64 có lẽ không phải là lựa chọn tối ưu cho đa số người dùng.
Trong thực tế, int64 thường được ứng dụng trong các trường hợp như UUID - nơi mà chúng ta không thực sự cần các phép tính số học phức tạp, mà chỉ cần khả năng so sánh bằng nhau. Cách tiếp cận tôi ưa chuộng trước đây là biểu diễn int64 dưới dạng chuỗi 8 byte. Phương pháp này không chỉ cho phép sử dụng làm khóa (key) duy nhất trong bảng băm (hash table), mà còn tránh được những mở rộng phức tạp không cần thiết. Trong thư viện pbc Lua binding, tôi đã áp dụng chính cách này cho kiểu dữ liệu fixed64.
Tuy nhiên gần đây, một yêu cầu mới xuất hiện khi có bạn lập trình viên mong muốn xử lý timestamps 64-bit trực tiếp trong dự án. Điều này đòi hỏi phải thực hiện các phép toán số học với số nguyên 64-bit. Dù cuối cùng nhóm đã quyết định chuyển sang dùng timestamps 32-bit để đơn giản hóa, nhưng câu hỏi làm thế nào hỗ trợ hiệu quả int64 trong Lua vẫn khiến tôi trăn trở.
Hiện tại, giải pháp phổ biến trong Luajit là tạo một userdata và nạp chồng các toán tử tương ứng. Ví dụ: bạn có thể dùng ffi.cast("int64_t",0)
để tạo số nguyên 64-bit bằng 0. Tuy nhiên ngoài vấn đề hiệu năng do việc sử dụng userdata, giải pháp này còn một điểm hạn chế quan trọng: khi dùng các giá trị int64 làm khóa trong bảng băm, hai giá trị có cùng số học nhưng lại là hai userdata khác nhau sẽ không được công nhận là trùng nhau.
Tôi cho rằng có một cách tối ưu hơn: Ở môi trường nền tảng 64-bit, chúng ta hoàn toàn có thể sử dụng lightuserdata để biểu diễn chính xác 64-bit dữ liệu mà không mất mát gì. Bằng cách gắn metatable cho lightuserdata, ta có thể định nghĩa lại các phép toán số học phù hợp. Điểm duy nhất chưa hài lòng là khi so sánh giữa int64 và kiểu number thông thường, Lua không tự động thực hiện chuyển đổi ngầm (nhưng các toán tử so sánh >, < vẫn hoạt động bình thường).
Chỉ mất 30 phút, tôi đã hoàn thiện ý tưởng này và đăng lên GitHub. Các bạn quan tâm có thể lấy về dùng thử. Thư viện này cung cấp duy nhất một API rõ ràng để khởi tạo số int64 từ kiểu number Lua hoặc từ chuỗi 8 byte theo chuẩn little-endian. Về bản chất, dữ liệu được lưu trữ dưới dạng lightuserdata (con trỏ 64-bit), nên thư viện chỉ tương thích với nền tảng 64-bit. Ngoài ra, lập trình viên có thể dùng trực tiếp API C lua_pushlightuserdata
để đẩy số nguyên 64-bit vào ngăn xếp.
Việc chuyển đổi ngược từ int64 về number Lua được thực hiện thông qua toán tử đánh dấu #. Đây là một giải pháp nhỏ gọn nhưng đầy hiệu quả, hy vọng sẽ mang lại nhiều tiện ích cho các bạn trong việc xử lý dữ liệu số lớn trong môi trường Lua.