forked from mstoykov/go-libarchive
/
reader.go
154 lines (132 loc) · 3.75 KB
/
reader.go
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
package archive
/*
#cgo pkg-config: libarchive
#include <archive.h>
#include <archive_entry.h>
#include <stdlib.h>
#include "reader.h"
*/
import "C"
import (
"errors"
"io"
"unsafe"
)
// Reader represents libarchive archive
type Reader struct {
archive *C.struct_archive
reader io.ReadSeeker // the io.Reader from which we Read
buffer []byte // buffer for the raw reading
index int64 // current reading index
}
// NewReader returns new Archive by calling archive_read_open
func NewReader(reader io.ReadSeeker) (r *Reader, err error) {
r = new(Reader)
r.buffer = make([]byte, 1024)
r.archive = C.archive_read_new()
C.archive_read_support_filter_all(r.archive)
C.archive_read_support_format_all(r.archive)
seek_callback := (*C.archive_seek_callback)(C.go_libarchive_seek)
C.archive_read_set_seek_callback(r.archive, seek_callback)
r.reader = reader
e := C.go_libarchive_open(r.archive, unsafe.Pointer(r))
err = codeToError(r.archive, int(e))
return
}
//export myopen
func myopen(archive *C.struct_archive, client_data unsafe.Pointer) C.int {
// actually write something
return ARCHIVE_OK
}
//export myclose
func myclose(archive *C.struct_archive, client_data unsafe.Pointer) C.int {
// actually write something
return ARCHIVE_OK
}
//export myread
func myread(archive *C.struct_archive, client_data unsafe.Pointer, block unsafe.Pointer) C.size_t {
reader := (*Reader)(client_data)
read, err := reader.reader.Read(reader.buffer)
if err != nil && err != ErrArchiveEOF {
// set error
read = -1
}
*(*uintptr)(block) = uintptr(unsafe.Pointer(&reader.buffer[0]))
return C.size_t(read)
}
//export myseek
func myseek(archive *C.struct_archive, client_data unsafe.Pointer, request C.int64_t, whence C.int) C.int64_t {
reader := (*Reader)(client_data)
offset, err := reader.reader.Seek(int64(request), int(whence))
if err != nil {
return C.int64_t(0)
}
return C.int64_t(offset)
}
// Next calls archive_read_next_header and returns an
// interpretation of the ArchiveEntry which is a wrapper around
// libarchive's archive_entry, or Err.
//
// ErrArchiveEOF is returned when there
// is no more to be read from the archive
func (r *Reader) Next() (ArchiveEntry, error) {
e := new(entryImpl)
errno := int(C.archive_read_next_header(r.archive, &e.entry))
err := codeToError(r.archive, errno)
if err != nil {
e = nil
}
return e, err
}
// Read calls archive_read_data which reads the current archive_entry.
// It acts as io.Reader.Read in any other aspect
func (r *Reader) Read(b []byte) (n int, err error) {
n = int(C.archive_read_data(r.archive, unsafe.Pointer(&b[0]), C.size_t(cap(b))))
if n == 0 {
err = ErrArchiveEOF
} else if 0 > n { // err
err = codeToError(r.archive, ARCHIVE_FAILED)
n = 0
}
r.index += int64(n)
return
}
// Seek sets the offset for the next Read to offset
func (r *Reader) Seek(offset int64, whence int) (int64, error) {
var abs int64
switch whence {
case 0:
abs = offset
case 1:
abs = int64(r.index) + offset
case 2:
abs = int64(len(r.buffer)) + offset
default:
return 0, errors.New("libarchive: SEEK [invalid whence]")
}
if abs < 0 {
return 0, errors.New("libarchive: SEEK [negative position]")
}
r.index = abs
return abs, nil
}
// Size returns compressed size of the current archive entry
func (r *Reader) Size() int {
return int(C.archive_filter_bytes(r.archive, C.int(0)))
}
// Free frees the resources the underlying libarchive archive is using
// calling archive_read_free
func (r *Reader) Free() error {
if C.archive_read_free(r.archive) == ARCHIVE_FATAL {
return ErrArchiveFatal
}
return nil
}
// Close closes the underlying libarchive archive
// calling archive read_cloe
func (r *Reader) Close() error {
if C.archive_read_close(r.archive) == ARCHIVE_FATAL {
return ErrArchiveFatal
}
return nil
}