1
1
'use strict'
2
2
3
3
const traverse = require ( 'pull-traverse' )
4
- const traverseSlice = require ( './traverse-slice' )
5
4
const UnixFS = require ( 'ipfs-unixfs' )
6
5
const CID = require ( 'cids' )
7
6
const pull = require ( 'pull-stream' )
8
7
const paramap = require ( 'pull-paramap' )
9
8
10
9
// Logic to export a single (possibly chunked) unixfs file.
11
10
module . exports = ( node , name , path , pathRest , resolve , size , dag , parent , depth , begin , end ) => {
12
- function getData ( node ) {
13
- try {
14
- const file = UnixFS . unmarshal ( node . data )
15
- return file . data || Buffer . alloc ( 0 )
16
- } catch ( err ) {
17
- throw new Error ( 'Failed to unmarshal node' )
18
- }
19
- }
20
-
21
- function visitor ( node ) {
22
- return pull (
23
- pull . values ( node . links ) ,
24
- paramap ( ( link , cb ) => dag . get ( new CID ( link . multihash ) , cb ) ) ,
25
- pull . map ( ( result ) => result . value )
26
- )
27
- }
28
-
29
11
const accepts = pathRest [ 0 ]
30
12
31
13
if ( accepts !== undefined && accepts !== path ) {
@@ -34,17 +16,7 @@ module.exports = (node, name, path, pathRest, resolve, size, dag, parent, depth,
34
16
35
17
const file = UnixFS . unmarshal ( node . data )
36
18
const fileSize = size || file . fileSize ( )
37
-
38
- let content
39
-
40
- if ( ! isNaN ( begin ) ) {
41
- content = traverseSlice ( node , dag , begin , end )
42
- } else {
43
- content = pull (
44
- traverse . depthFirst ( node , visitor ) ,
45
- pull . map ( getData )
46
- )
47
- }
19
+ const content = streamBytes ( dag , node , fileSize , findByteRange ( fileSize , begin , end ) )
48
20
49
21
return pull . values ( [ {
50
22
depth : depth ,
@@ -56,3 +28,118 @@ module.exports = (node, name, path, pathRest, resolve, size, dag, parent, depth,
56
28
type : 'file'
57
29
} ] )
58
30
}
31
+
32
+ function findByteRange ( fileSize , begin , end ) {
33
+ if ( ! begin ) {
34
+ begin = 0
35
+ }
36
+
37
+ if ( ! end || end > fileSize ) {
38
+ end = fileSize
39
+ }
40
+
41
+ if ( begin < 0 ) {
42
+ begin = fileSize + begin
43
+ }
44
+
45
+ if ( end < 0 ) {
46
+ end = fileSize + end
47
+ }
48
+
49
+ return {
50
+ begin, end
51
+ }
52
+ }
53
+
54
+ function streamBytes ( dag , node , fileSize , { begin, end } ) {
55
+ if ( begin === end ) {
56
+ return pull . empty ( )
57
+ }
58
+
59
+ let streamPosition = 0
60
+
61
+ function getData ( { node, start } ) {
62
+ if ( ! node || ! node . data ) {
63
+ return
64
+ }
65
+
66
+ try {
67
+ const file = UnixFS . unmarshal ( node . data )
68
+
69
+ if ( ! file . data ) {
70
+ return
71
+ }
72
+
73
+ const block = extractDataFromBlock ( file . data , start , begin , end )
74
+
75
+ return block
76
+ } catch ( err ) {
77
+ throw new Error ( 'Failed to unmarshal node' )
78
+ }
79
+ }
80
+
81
+ function visitor ( { node } ) {
82
+ const file = UnixFS . unmarshal ( node . data )
83
+
84
+ // work out which child nodes contain the requested data
85
+ const filteredLinks = node . links
86
+ . map ( ( link , index ) => {
87
+ const child = {
88
+ link : link ,
89
+ start : streamPosition ,
90
+ end : streamPosition + file . blockSizes [ index ]
91
+ }
92
+
93
+ streamPosition = child . end
94
+
95
+ return child
96
+ } )
97
+ . filter ( ( child , index ) => {
98
+ return ( begin >= child . start && begin < child . end ) || // child has begin byte
99
+ ( end > child . start && end <= child . end ) || // child has end byte
100
+ ( begin < child . start && end > child . end ) // child is between begin and end bytes
101
+ } )
102
+
103
+ if ( filteredLinks . length ) {
104
+ // move stream position to the first node we're going to return data from
105
+ streamPosition = filteredLinks [ 0 ] . start
106
+ }
107
+
108
+ return pull (
109
+ pull . values ( filteredLinks ) ,
110
+ paramap ( ( child , cb ) => {
111
+ dag . get ( new CID ( child . link . multihash ) , ( error , result ) => cb ( error , {
112
+ start : child . start ,
113
+ end : child . end ,
114
+ node : result && result . value
115
+ } ) )
116
+ } )
117
+ )
118
+ }
119
+
120
+ return pull (
121
+ traverse . depthFirst ( {
122
+ node,
123
+ start : 0 ,
124
+ end : fileSize
125
+ } , visitor ) ,
126
+ pull . map ( getData ) ,
127
+ pull . filter ( Boolean )
128
+ )
129
+ }
130
+
131
+ function extractDataFromBlock ( block , streamPosition , begin , end ) {
132
+ const blockLength = block . length
133
+
134
+ if ( end - streamPosition < blockLength ) {
135
+ // If the end byte is in the current block, truncate the block to the end byte
136
+ block = block . slice ( 0 , end - streamPosition )
137
+ }
138
+
139
+ if ( begin > streamPosition && begin < ( streamPosition + blockLength ) ) {
140
+ // If the start byte is in the current block, skip to the start byte
141
+ block = block . slice ( begin - streamPosition )
142
+ }
143
+
144
+ return block
145
+ }
0 commit comments