Registry / RepositoryTypeScriptRustPython
Rollpie
ReadmeFiles
Versions
Info
Download
0.1.098.9 KB2026-09-14
Version
0.1.0
Copyright
Rollpie, Irohabook
Publisher
math
Published
2026-09-14
Size
98.9 KB
Downloads
1
Checksum
bdac84a1e4fe4dd3505b93c2781b1f15833b5189c2e4ee976225fa3c512885cc
Dependencies
None

archive.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
//! ZIP に書いて読み戻すところ

use folio::zip::{Archive, Writer};
use std::path::{Path, PathBuf};


/// 試しに使うファイルの置き場。target の下なので消えても差し支えない
fn place(name: &str) -> PathBuf {
    let mut path = PathBuf::from("target");
    path.push(format!("archive-{name}.zip"));
    path
}


/// 決まった並びの疑似乱数
fn noise(count: usize, seed: u64) -> Vec<u8> {
    let mut value = seed;
    let mut result = Vec::with_capacity(count);

    for _ in 0..count {
        value = value.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
        result.push((value >> 33) as u8);
    }

    result
}


#[test]
fn parts_come_back_as_they_were_put_in() {
    let path = place("plain");
    let text = "<?xml version=\"1.0\"?><Types/>".as_bytes().to_vec();
    let long = "<si><t>あ</t></si>".repeat(5000).into_bytes();
    let image = noise(4000, 5);

    let mut writer = Writer::create(&path).unwrap();
    writer.add("[Content_Types].xml", &text).unwrap();
    writer.add("xl/sharedStrings.xml", &long).unwrap();
    writer.add_stored("xl/media/image1.png", &image).unwrap();
    writer.add("日本語の名前.xml", "こんにちは".as_bytes()).unwrap();
    writer.finish().unwrap();

    let mut archive = Archive::open(&path).unwrap();
    assert_eq!(archive.entry().len(), 4);
    assert!(archive.has("xl/sharedStrings.xml"));
    assert_eq!(archive.read("[Content_Types].xml").unwrap(), text);
    assert_eq!(archive.read("xl/sharedStrings.xml").unwrap(), long);
    assert_eq!(archive.read("xl/media/image1.png").unwrap(), image);
    assert_eq!(archive.read("日本語の名前.xml").unwrap(), "こんにちは".as_bytes());
}


#[test]
fn a_part_of_unknown_length_can_be_written() {
    let path = place("stream");
    let mut writer = Writer::create(&path).unwrap();
    writer.start("xl/worksheets/sheet1.xml").unwrap();
    let mut whole = Vec::new();

    for row in 0..50000 {
        let line = format!("<row r=\"{row}\"><c r=\"A{row}\"><v>{row}</v></c></row>");
        writer.write(line.as_bytes()).unwrap();
        whole.extend_from_slice(line.as_bytes());
    }

    writer.close().unwrap();
    writer.finish().unwrap();

    let mut archive = Archive::open(&path).unwrap();
    assert_eq!(archive.read("xl/worksheets/sheet1.xml").unwrap(), whole);
}


#[test]
fn a_big_part_uses_zip64() {
    let path = place("zip64");
    let mut writer = Writer::create(&path).unwrap();
    writer.start("big.bin").unwrap();
    let mut whole = Vec::new();

    // 縮まらない並びを 12MB。8MB を超えるので ZIP64 のヘッダになる
    for step in 0..600 {
        let piece = noise(20000, step + 1);
        writer.write(&piece).unwrap();
        whole.extend_from_slice(&piece);
    }

    writer.close().unwrap();
    writer.finish().unwrap();

    let mut archive = Archive::open(&path).unwrap();
    assert_eq!(archive.entry()[0].uncompressed, whole.len() as u64);
    assert_eq!(archive.read("big.bin").unwrap(), whole);
}


#[test]
fn a_part_can_be_read_a_piece_at_a_time() {
    let path = place("piece");
    let whole = "<sheetData>".to_string() + &"<row/>".repeat(200000) + "</sheetData>";
    let image = noise(300000, 23);

    let mut writer = Writer::create(&path).unwrap();
    writer.add("sheet.xml", whole.as_bytes()).unwrap();
    writer.add_stored("image.png", &image).unwrap();
    writer.finish().unwrap();

    let mut archive = Archive::open(&path).unwrap();

    for (name, want) in [("sheet.xml", whole.as_bytes().to_vec()), ("image.png", image)] {
        let mut reader = archive.reader(name).unwrap();
        let mut back = Vec::new();

        while let Some(piece) = reader.next().unwrap() {
            back.extend_from_slice(&piece);
        }

        assert_eq!(back, want, "{name}");
    }
}


#[test]
fn an_entry_can_be_copied_without_unpacking_it() {
    let source_path = place("source");
    let target_path = place("target");
    let long = "<si><t>くりかえし</t></si>".repeat(3000).into_bytes();

    let mut writer = Writer::create(&source_path).unwrap();
    writer.add("keep.xml", &long).unwrap();
    writer.add("drop.xml", b"<drop/>").unwrap();
    writer.finish().unwrap();

    let mut source = Archive::open(&source_path).unwrap();
    let packed = source.raw("keep.xml").unwrap().len();

    let mut writer = Writer::create(&target_path).unwrap();
    writer.copy(&mut source, "keep.xml").unwrap();
    writer.add("new.xml", b"<new/>").unwrap();
    writer.finish().unwrap();

    let mut target = Archive::open(&target_path).unwrap();
    assert_eq!(target.entry().len(), 2);
    assert!(!target.has("drop.xml"));
    assert_eq!(target.read("keep.xml").unwrap(), long);

    // 展開も圧縮もしていないので、縮んだ長さがそのまま同じになる
    assert_eq!(target.raw("keep.xml").unwrap().len(), packed);
}


#[test]
fn a_missing_part_is_an_error() {
    let path = place("missing");
    let mut writer = Writer::create(&path).unwrap();
    writer.add("one.xml", b"<one/>").unwrap();
    writer.finish().unwrap();

    let mut archive = Archive::open(&path).unwrap();
    assert!(archive.read("two.xml").is_err());
}


#[test]
fn a_file_that_is_not_a_zip_is_an_error() {
    let path = Path::new("target/archive-broken.zip");
    std::fs::write(path, "これは ZIP ではありません").unwrap();
    assert!(Archive::open(path).is_err());
}