PyOxidizer đã xác định định dạng dữ liệu tùy chỉnh để lưu trữ tài nguyên hữu ích cho việc thực thi trình thông dịch Python. Chúng tôi gọi định dạng dữ liệu này là tài nguyên đóng gói Python
Cách nó hoạt động là một số nhà sản xuất thu thập tài nguyên theo yêu cầu của trình thông dịch Python. Các tài nguyên này bao gồm nguồn mô-đun Python và mã byte, tệp dữ liệu/tài nguyên không phải mô-đun, mô-đun mở rộng và thư viện dùng chung. Siêu dữ liệu về các tài nguyên này và đôi khi chính dữ liệu tài nguyên thô được tuần tự hóa thành cấu trúc dữ liệu nhị phân
Vào thời gian chạy trình thông dịch Python, cấu trúc dữ liệu này được tải [nó có thể được nhúng trong tệp nhị phân hoặc tồn tại dưới dạng tệp độc lập] và được phân tích cú pháp. Trình tìm đường dẫn Meta Python tùy chỉnh [ OxidizedFinder từ oxidized_importer Python Extension] then uses the parsed data structure to power Python module importing.
Chức năng này tương tự như sử dụng tệp .zip
để giữ các mô-đun Python. Tuy nhiên, cấu trúc dữ liệu tài nguyên đóng gói của Python tiên tiến hơn nhiều
Thực hiện¶
Việc triển khai chuẩn của trình ghi và trình phân tích cú pháp của cấu trúc dữ liệu này tồn tại trong thùng rỉ sét của python-packed-resources
. Trang chủ chuẩn của thùng này là https. //github. com/indygreg/PyOxidizer/tree/main/python-packed-resources
thùng này được xuất bản để thùng. io tại https. // thùng. io/crates/python-packed-resources
Sự chỉ rõ¶
Từ mức cao, cấu trúc dữ liệu xác định một tài nguyên có thể lặp lại. Tài nguyên là một thực thể có tên, siêu dữ liệu và các trường blob. Thông thường, tài nguyên phổ biến nhất là mô-đun/gói Python. Nhưng các loại tài nguyên khác [chẳng hạn như thư viện dùng chung] được xác định
8 byte đầu tiên của cấu trúc dữ liệu là một tiêu đề ma thuật xác định nội dung là cấu trúc dữ liệu của chúng tôi và phiên bản của nó. 7 byte đầu tiên là pyembed
và 1 byte sau biểu thị một phiên bản. Ngữ nghĩa của từng phiên bản được biểu thị trong các phần bên dưới
Bố cục cấp cao¶
Từ cấp độ cao, định dạng tuần tự hóa bao gồm
- Một tiêu đề toàn cầu mô tả tải trọng tổng thể
- Một chỉ mục mô tả các phần blob có trong tải trọng
- Một chỉ mục mô tả từng tài nguyên và nội dung của nó
- Một loạt các phần blob chứa dữ liệu được tham chiếu bởi chỉ mục tài nguyên
Một tài nguyên bao gồm nhiều trường khác nhau mô tả nó. Ví dụ về các trường bao gồm tên tài nguyên, mã nguồn và mã byte. Chỉ mục tài nguyên mô tả trường nào có mặt và tìm thấy chúng ở đâu trong tải trọng
Nội dung thực tế của các trường [e. g. byte thô chứa mã nguồn] được lưu trữ trong các phần dành riêng cho trường sau chỉ mục. Mỗi trường có phần riêng và dữ liệu cho tất cả các tài nguyên được lưu trữ cạnh nhau. e. g. bạn sẽ có tất cả dữ liệu cho tên tài nguyên theo sau là tất cả dữ liệu cho mã nguồn mô-đun
Định dạng dữ liệu cấp thấp được mô tả bên dưới. Tất cả các số nguyên là little-endian
13 byte đầu tiên sau tiêu đề ma thuật biểu thị tiêu đề chung. Tiêu đề toàn cầu bao gồm
- Một
u8
biểu thị số phần blob,blob_sections_count
- Một
u32
biểu thị độ dài của chỉ mục blob,blob_index_length
- Một
u32
biểu thị tổng số tài nguyên trong dữ liệu này,resources_count
- Một
u32
biểu thị độ dài của chỉ mục tài nguyên,python-packed-resources
0
Theo sau tiêu đề toàn cầu là chỉ mục blob. Chỉ mục blob mô tả các phần blob khác nhau có trong tải trọng sau chỉ mục tài nguyên
Mỗi mục trong chỉ mục blob bao gồm một tập hợp các trường xác định siêu dữ liệu về từng phần blob một cách hợp lý. Điều này được mã hóa bằng điểm đánh dấu bắt đầu mục nhập u8
, theo sau là giá trị loại trường N u8
và siêu dữ liệu tương ứng của chúng, theo sau là điểm đánh dấu kết thúc mục nhập u8
. Chỉ mục blob được chấm dứt bởi điểm đánh dấu cuối chỉ mục u8
. Tổng số byte trong chỉ mục blob bao gồm cả điểm đánh dấu cuối chỉ mục phải là blob_index_length
Theo sau chỉ mục blob là chỉ mục tài nguyên. Mỗi mục nhập trong chỉ mục này xác định một tập hợp siêu dữ liệu thưa thớt mô tả một tài nguyên. Các mục bao gồm một loạt u8
phần siêu dữ liệu xác định, theo sau là các mô tả bổ sung theo lĩnh vực cụ thể. Ví dụ: giá trị của python-packed-resources
7 biểu thị độ dài của tên tài nguyên và ngay sau đó là giá trị python-packed-resources
8 giữ độ dài nói trên. Xem phần bên dưới để biết từng trường được chỉ mục này theo dõi
Theo sau chỉ mục tài nguyên là dữ liệu blob. Dữ liệu blob bao gồm một cách hợp lý các phần khác nhau chứa dữ liệu cho các trường khác nhau cho các tài nguyên khác nhau. Nhưng không có cấu trúc bên trong hoặc dải phân cách. tất cả các đốm màu riêng lẻ được đặt cạnh nhau
Các loại trường đốm màu¶
Chỉ mục Blob cho phép phân bổ một tập hợp siêu dữ liệu thưa thớt với mỗi mục nhập phần blob. Loại siêu dữ liệu được chuyển tải được xác định bởi một u8
. Một số loại trường có siêu dữ liệu bổ sung sau trường đó
Các loại trường khác nhau và ngữ nghĩa của chúng tuân theo
pyembed
0Kết thúc chỉ mục. Trường này chỉ ra rằng không còn mục nhập chỉ mục blob nào nữa và chúng tôi đã đạt đến cuối chỉ mục blob. pyembed
1Bắt đầu mục blob. Gặp phải giá trị này báo hiệu sự bắt đầu của một phần blob mới. Từ quan điểm đặc điểm kỹ thuật, điều này không bắt buộc. Nhưng nó giúp đảm bảo trạng thái trình phân tích cú pháp. pyembed
2Mục nhập phần cuối của đốm màu. Gặp phải giá trị này báo hiệu sự kết thúc của định nghĩa phần blob hiện tại. u8
gặp phải tiếp theo trong chỉ mục phải là pyembed
1 để biểu thị một mục mới hoặc pyembed
0 để biểu thị kết thúc chỉ mục. python-packed-resources
7Loại trường tài nguyên. Trường này xác định trường tài nguyên nào mà phần blob này đang giữ dữ liệu cho. Một u8
theo sau cái này sẽ chứa giá trị loại trường tài nguyên [xem phần bên dưới]. pyembed
8Chiều dài tải trọng thô. Trường này xác định độ dài thô tính bằng byte của phần blob trong tải trọng. pyembed
9 chứa độ dài đó sẽ ngay lập tức theo sau u8
này. u8
1Cơ chế đệm bên trong. Trường này xác định phần đệm bên trong giữa các phần tử trong phần blob. Theo sau u8
này là một u8
khác biểu thị cơ chế đệm
pyembed
1 biểu thị không có phần đệm. python-packed-resources
7 biểu thị phần đệm NULL [một pyembed
0 giữa các phần tử]
Nếu không có, không có phần đệm nào được giả định. Nếu dữ liệu tải trọng bao gồm một cách hợp lý các tài nguyên rời rạc [e. g. tệp tài nguyên gói Python], thì phần đệm cũng áp dụng cho các phần tử phụ này
Các loại trường tài nguyên¶
Chỉ mục tài nguyên cho phép phân bổ một tập hợp siêu dữ liệu thưa thớt với mọi tài nguyên. Một u8
cho biết siêu dữ liệu nào đang được chuyển tải. Một số loại trường có siêu dữ liệu bổ sung theo sau u8
8 này xác định thêm trường. Các giá trị của từng loại siêu dữ liệu được xác định tuân theo
pyembed
0Kết thúc chỉ mục. Loại đặc biệt để biểu thị phần cuối của một chỉ mục. pyembed
1Bắt đầu nhập tài nguyên. Báo hiệu sự khởi đầu của một tài nguyên mới. Từ quan điểm đặc điểm kỹ thuật, điều này không bắt buộc. Nhưng nó giúp đảm bảo trạng thái trình phân tích cú pháp. python-packed-resources
7Hương vị tài nguyên. Khai báo loại tài nguyên mà mục nhập này đại diện. Một u8
xác định hương vị tài nguyên ngay sau byte này. Xem phần bên dưới để biết các hương vị tài nguyên hợp lệ
Trường này không được dùng trong phiên bản 2 để thay thế cho các trường riêng lẻ thể hiện sự hiện diện của một loại tài nguyên. [Xem các trường bắt đầu từ blob_sections_count
3. ]
pyembed
2Kết thúc mục nhập tài nguyên. u8
gặp phải tiếp theo trong chỉ mục phải là điểm cuối của chỉ mục hoặc điểm bắt đầu của điểm đánh dấu tài nguyên. pyembed
8Tên tài nguyên. Một python-packed-resources
8 biểu thị độ dài tính bằng byte của tên tài nguyên ngay sau byte này. Tên tài nguyên phải là UTF-8 hợp lệ. u8
1Cờ gói hàng. Nếu gặp phải, tài nguyên được xác định là gói Python. blob_sections_count
9Cờ gói không gian tên. Nếu gặp phải, tài nguyên được xác định là gói không gian tên Python. u32
0Mã nguồn mô-đun Python trong bộ nhớ. Một u32
biểu thị độ dài tính bằng byte của mã nguồn của mô-đun ngay sau byte này. u32
2Mã byte mô-đun Python trong bộ nhớ. Một u32
biểu thị độ dài tính bằng byte của mã byte của mô-đun ngay sau byte này. u32
4Mô-đun Python trong bộ nhớ được tối ưu hóa mã byte cấp 1. Một u32
biểu thị độ dài tính bằng byte của mã byte cấp 1 tối ưu hóa của mô-đun ngay sau byte này. u32
6Mô-đun Python trong bộ nhớ được tối ưu hóa mã byte cấp 2. Tương tự như trước, ngoại trừ tối ưu hóa mã byte cấp 2. u32
7Thư viện chia sẻ mô-đun mở rộng Python trong bộ nhớ. Một u32
biểu thị độ dài tính bằng byte của mã máy của mô-đun mở rộng ngay sau byte này. u32
9Dữ liệu tài nguyên Python trong bộ nhớ. Nếu gặp phải, mô-đun/gói chứa tệp tài nguyên không phải mô-đun và số lượng tài nguyên được chứa trong u32
ngay sau đó. Theo sau u32
này là một mảng blob_index_length
2 biểu thị tên tài nguyên và kích thước tải trọng cho từng tài nguyên trong gói này. blob_index_length
3Tài nguyên phân phối Python trong bộ nhớ. Xác định tài nguyên được truy cập từ blob_index_length
4 API. Nếu gặp phải, mô-đun/gói chứa siêu dữ liệu phân phối mô tả gói. Số lượng tệp được mô tả được chứa trong một u32
ngay sau byte này. Theo sau u32
này là một mảng blob_index_length
2 biểu thị tên tệp phân phối và kích thước tải trọng cho từng tệp ảo trong bản phân phối này. blob_index_length
8Thư viện chia sẻ trong bộ nhớ. Nếu được đặt, tài nguyên này là thư viện dùng chung chứ không phải mô-đun Python. Trường tên tài nguyên là tên của thư viện được chia sẻ này, với phần mở rộng tệp [vì nó sẽ xuất hiện trong siêu dữ liệu của trình tải nhị phân động để biểu thị sự phụ thuộc của thư viện]. Một pyembed
9 biểu thị độ dài tính bằng byte của dữ liệu thư viện dùng chung theo sau. Thư viện dùng chung này phải được tải từ bộ nhớ. u32
0Tên phụ thuộc thư viện được chia sẻ. Trường này cho biết tên của các thư viện dùng chung mà thực thể này phụ thuộc vào. Số lượng tên thư viện được chứa trong một python-packed-resources
8 ngay sau byte này. Theo sau python-packed-resources
8 này là một mảng python-packed-resources
8 biểu thị độ dài của tên thư viện cho mỗi phụ thuộc thư viện dùng chung. Mỗi phụ thuộc thư viện dùng chung được mô tả có thể được mô tả hoặc không được mô tả bởi các mục nhập khác trong cấu trúc dữ liệu này. u32
4Đường dẫn hệ thống tệp tương đối đến mã nguồn mô-đun Python. Một u32
giữ độ dài tính bằng byte của đường dẫn hệ thống tệp được mã hóa trong mã hóa đường dẫn tệp gốc nền tảng theo sau. Mã nguồn của mô-đun Python sẽ được đọc từ một tệp tại đường dẫn này. u32
6Đường dẫn hệ thống tệp tương đối đến mã byte mô-đun Python. Tương tự như phần trước ngoại trừ đường dẫn hệ thống tệp chứa mã byte của mô-đun Python. u32
7Đường dẫn hệ thống tệp tương đối đến mã byte mô-đun Python ở mức tối ưu hóa 1. Tương tự như trước đó ngoại trừ những gì đang được trỏ đến. u32
8Đường dẫn hệ thống tệp tương đối đến mã byte mô-đun Python ở mức tối ưu hóa 2. Tương tự như trước đó ngoại trừ những gì đang được trỏ đến. u32
9Đường dẫn hệ thống tệp tương đối đến thư viện chia sẻ mô-đun mở rộng Python. Tương tự như phần trước ngoại trừ tệp chứa mô-đun mở rộng Python có thể tải dưới dạng thư viện dùng chung. resources_count
0Đường dẫn hệ thống tệp tương đối đến tài nguyên gói Python. Số lượng tài nguyên được chứa trong một u32
ngay sau đó. Theo sau u32
này là một mảng resources_count
3 biểu thị tên tài nguyên và đường dẫn hệ thống tệp tới từng tài nguyên trong gói này. resources_count
4Đường dẫn hệ thống tệp tương đối đến tài nguyên phân phối Python
Xác định tài nguyên được truy cập từ blob_index_length
4 API. Nếu gặp phải, mô-đun/gói chứa siêu dữ liệu phân phối mô tả gói. Số lượng tệp được mô tả được chứa trong một u32
ngay sau byte này. Theo sau u32
này là một mảng resources_count
3 biểu thị tên tệp phân phối và đường dẫn hệ thống tệp đến tệp phân phối đó
blob_sections_count
3Là cờ mô-đun Python. Nếu được đặt, tài nguyên này chứa dữ liệu cho gói hoặc mô-đun Python có thể nhập. Dữ liệu tài nguyên được liên kết với các gói Python và thuộc loại này. Cờ mô-đun mở rộng dựng sẵn u32
0Is. Loại này đại diện cho một mô-đun mở rộng Python được tích hợp sẵn [được biên dịch thành] chính trình thông dịch hoặc được cung cấp cho trình thông dịch thông qua u32
1 sao cho nó phải được nhập bằng trình nhập nội trang. u32
2Cờ mô-đun Python bị đóng băng. Loại này đại diện cho một mô-đun Python có mã byte bị đóng băng và được cung cấp cho trình thông dịch Python thông qua mảng u32
3 và phải được nhập bằng trình nhập bị đóng băng. u32
4Là cờ mở rộng Python. Loại này đại diện cho một phần mở rộng Python đã biên dịch. Các tiện ích mở rộng có các yêu cầu cụ thể về cách chúng được tải và được phân biệt với các mô-đun Python thông thường. u32
5Là cờ thư viện dùng chung. Loại này đại diện cho một thư viện dùng chung có thể được tải vào một quy trình. u32
6Là cờ dữ liệu tên tệp utf-8. Loại này đại diện cho một tên tệp tùy ý. Tên tài nguyên là tên tệp được mã hóa UTF-8 của tệp mà tài nguyên này đại diện. Dữ liệu của tệp được nhúng trong bộ nhớ hoặc được tham chiếu qua tham chiếu đường dẫn tương đối. u32
7Dữ liệu tệp là cờ thực thi
Nếu được đặt, tệp tùy ý mà tài nguyên này theo dõi sẽ được đánh dấu là có thể thực thi được
u32
8Dữ liệu tệp nhúng
Nếu có, tài nguyên phải là tài nguyên tệp và trường này chứa dữ liệu tệp thô của nó trong bộ nhớ
Một pyembed
9 chứa độ dài của dữ liệu được nhúng theo sau trường này
python-packed-resources
00Dữ liệu tệp đường dẫn tương đối UTF-8
Nếu có, tài nguyên phải là tài nguyên tệp và trường này xác định đường dẫn tương đối chứa dữ liệu của tệp đó. Tên tệp đường dẫn tương đối được mã hóa UTF-8
Một u32
biểu thị độ dài của đường dẫn tương đối UTF-8 [tính bằng byte] theo sau
Hương vị tài nguyên¶
Quan trọng
Các hương vị tài nguyên được liệt kê không được dùng nữa sau phiên bản 1. Thay vào đó, bạn nên sử dụng các trường riêng lẻ để thể hiện danh tính tài nguyên
Định dạng dữ liệu cho phép xác định các loại/hương vị khác nhau của tài nguyên. Hương vị này của tài nguyên được xác định bởi một u8
. Các hương vị được công bố là
pyembed
0Không có hương vị. không nên gặp. pyembed
1Mô-đun/gói Python. Điều này tương đương với trường tài nguyên blob_sections_count
3 được đặt. python-packed-resources
7Mô-đun mở rộng Python tích hợp. Điều này tương đương với trường tài nguyên u32
0 được đặt. pyembed
8Mô-đun Python đông lạnh. Điều này tương đương với trường tài nguyên u32
2 được đặt. u8
1Phần mở rộng Python. Điều này tương đương với trường tài nguyên u32
4 được đặt. blob_sections_count
9Thư viện chia sẻ. Điều này tương đương với trường tài nguyên u32
5 được đặtĐịnh dạng python-packed-resources
14¶
Định dạng dữ liệu tài nguyên đóng gói được phát hành/chính thức hóa ban đầu
Hỗ trợ các loại trường tài nguyên lên đến và bao gồm resources_count
4
Định dạng python-packed-resources
16¶
Phiên bản 2 của định dạng dữ liệu tài nguyên đóng gói
Phiên bản này giới thiệu các giá trị loại trường blob_sections_count
3 đến u32
5. Loại trường hương vị tài nguyên [python-packed-resources
7] không được dùng nữa và thay vào đó nên sử dụng các loại trường riêng lẻ biểu thị các loại tài nguyên
[PyOxidizer đã loại bỏ mã thời gian chạy khi xem loại trường python-packed-resources
7 khi định dạng này được giới thiệu. ]
Định dạng python-packed-resources
21¶
Phiên bản 3 của định dạng dữ liệu tài nguyên đóng gói
Phiên bản này giới thiệu các giá trị loại trường u32
6 đến python-packed-resources
00
Các trường này cung cấp khả năng cho tài nguyên tự xác định chính nó dưới dạng tên tệp tùy ý và cho dữ liệu tệp tùy ý được nhúng trong cấu trúc dữ liệu hoặc được tham chiếu qua đường dẫn tương đối
Không giống như các trường trước sử dụng mã hóa gốc hệ điều hành của đường dẫn hệ thống tệp [u8
8 trên POSIX và python-packed-resources
25 trên Windows], đường dẫn cho các trường mới này sử dụng UTF-8. Điều này không thể đại diện cho tất cả các đường dẫn hợp lệ trên tất cả các nền tảng. Nhưng nó có thể di động và hoạt động cho hầu hết các con đường gặp phải trong tự nhiên
Cân nhắc thiết kế¶
Thiết kế của định dạng dữ liệu tài nguyên đóng gói bị ảnh hưởng bởi một số cân nhắc
Hiệu suất là một cân nhắc quan trọng. Chúng tôi muốn mọi thứ diễn ra nhanh nhất có thể. Các kích thước có thể ảnh hưởng đến hiệu suất bao gồm thời gian phân tích cú pháp, kích thước tải trọng và các mẫu truy cập I/O
Tải trọng được thiết kế sao cho dữ liệu chỉ mục ở đầu để người đọc chỉ phải đọc một lát dữ liệu liền kề để hiểu đầy đủ dữ liệu bên trong. Điều này trái ngược với việc nhảy xung quanh toàn bộ cấu trúc dữ liệu để trích xuất siêu dữ liệu của dữ liệu bên trong. Điều này có nghĩa là chúng tôi chỉ cần trang trong một phần nhỏ của toàn bộ cấu trúc dữ liệu sao lưu để khởi tạo trình nhập tùy chỉnh của chúng tôi. Ngoài ra, dữ liệu chỉ mục được đọc tuần tự. I/O tuần tự phải luôn nhanh hơn I/O truy cập ngẫu nhiên
x86 là endian nhỏ, vì vậy chúng tôi sử dụng số nguyên endian nhỏ để chúng tôi không cần lãng phí các chu kỳ khi chuyển đổi endian
Chúng tôi lưu trữ tất cả dữ liệu cho cùng một trường cạnh nhau trong cấu trúc dữ liệu. Điều này trái ngược với việc nói đóng gói tất cả dữ liệu của tài nguyên A sau đó là tài nguyên B, v.v. Chúng tôi làm điều này để giúp tối đa hóa vị trí cho dữ liệu tương tự. Điều này có thể giúp cải thiện hiệu suất vì thường cùng một trường cho nhiều tài nguyên được truy cập cùng nhau. e. g. một nhà nhập khẩu sẽ truy cập vào một loạt các mục nhập mã byte của mô-đun cùng một lúc. Địa phương này giúp giảm thiểu số lượng trang phải đọc. Địa phương cũng có thể giúp mang lại tỷ lệ nén cao hơn
Mọi thứ được thiết kế để tạo điều kiện cho người đọc tận dụng 0 bản sao. Nếu một đầu đọc có cấu trúc dữ liệu trong bộ nhớ, chúng tôi không muốn yêu cầu nó sao chép bộ nhớ để tham chiếu các mục nhập. Trong Rust speak, chúng tôi sẽ có thể lưu giữ tài liệu tham khảo python-packed-resources
26 ở mọi nơi
Không có tổng kiểm tra dữ liệu vì chúng tôi không muốn phát sinh chi phí I/O để đọc toàn bộ đốm màu. Nó có thể được thêm vào như một tính năng tùy chọn
Các tính năng tiềm năng trong tương lai¶
Cấu trúc dữ liệu này đủ mạnh để PyOxidizer sử dụng để cấp nguồn cho việc nhập mọi mô-đun Python được trình thông dịch Python sử dụng. Tuy nhiên, có nhiều khía cạnh khác nhau có thể được cải thiện
Nén¶
Một lĩnh vực tiềm năng để tối ưu hóa là sử dụng nén chung. Các trường khác nhau sẽ nén tốt - ở chế độ phát trực tuyến hoặc bằng cách sử dụng từ điển nén. Tất nhiên, quá trình nén sẽ làm suy yếu bản sao 0. Nhưng trong những môi trường mà chúng tôi muốn tối ưu hóa kích thước, điều đó có thể là mong muốn
Tính di động của nền tảng¶
Hiện tại, các đường dẫn hệ thống tệp được mã hóa dưới dạng nền tảng gốc. Điều đó có nghĩa là u8
8 trên POSIX và python-packed-resources
25 trên Windows. Đây không phải là xách tay
Hầu hết các tên tệp có khả năng an toàn ASCII hoặc UTF-8. Đối với trường hợp phổ biến khi chúng tôi không cần tên tệp gốc nền tảng để duy trì sự khác biệt về mã hóa tinh tế, chúng tôi có thể biểu thị đường dẫn dưới dạng một loại chuỗi đơn giản hơn