1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.xnap.plugin.opennap.net;
21
22 import java.io.File;
23
24 import org.xnap.transfer.Segment;
25
26 /***
27 * <p>The end of the segment can change through a split.
28 */
29 public class OpenNapSegment implements Cloneable, Segment {
30
31
32
33 /***
34 * The minimun size for a single segment.
35 */
36 public static final long MIN_SEGEMENT_SIZE = 100 * 1024;
37
38
39
40 private OpenNapSegmentData data;
41 private long end;
42 private long total;
43 private long transferred;
44 private OpenNapSegment previous;
45 private OpenNapSegment next;
46 private OpenNapDownload download;
47
48
49
50 public OpenNapSegment(long start, long end, long total)
51 {
52 data = new OpenNapSegmentData();
53
54 data.start = start;
55 this.end = end;
56 this.total = total;
57 }
58
59 /***
60 * Creates a new segment based on resume data. The segment spans from
61 * <code>data.start</code> to <code>total</code>.
62 */
63 public OpenNapSegment(OpenNapSegmentData data, long total)
64 {
65 this.data = data;
66
67 this.transferred = (data.file != null) ? data.file.length() : 0;
68 this.end = total;
69 this.total = total;
70 }
71
72
73
74 public Object clone()
75 {
76 OpenNapSegment n = new OpenNapSegment(data.start, end, total);
77
78 n.data.file = data.file;
79 n.data.mergeFailCount = data.mergeFailCount;
80 n.download = download;
81 n.previous = previous;
82 n.next = next;
83 n.transferred = transferred;
84
85 return n;
86 }
87
88 public int getAvailability()
89 {
90 return (download != null) ? 128 : 0;
91 }
92
93 OpenNapSegmentData getData()
94 {
95 return data;
96 }
97
98 public long getEnd()
99 {
100 return this.end;
101 }
102
103 /***
104 * Returns the download currently downloading the segment.
105 *
106 * @return null, if this segment is currently idle
107 */
108 public OpenNapDownload getDownload()
109 {
110 return download;
111 }
112
113 /***
114 * Returns the download offset that should be sent to the peer.
115 */
116 public long getDownloadOffset()
117 {
118 return data.start + transferred - getOverlap();
119 }
120
121 /***
122 * Returns the file offset to seek to when matching the overlap
123 */
124 public long getFileOffset()
125 {
126 return transferred - getOverlap();
127 }
128
129 public File getFile()
130 {
131 return data.file;
132 }
133
134 public OpenNapSegment getNext()
135 {
136 return next;
137 }
138
139 /***
140 * Returns the number of bytes that need to be matched.
141 */
142 public int getOverlap()
143 {
144 return (transferred < OpenNapSegmentManager.OVERLAP)
145 ? (int)transferred
146 : OpenNapSegmentManager.OVERLAP;
147 }
148
149 public OpenNapSegment getPrevious()
150 {
151 return previous;
152 }
153
154 public long getStart()
155 {
156 return data.start;
157 }
158
159 public long getTotal()
160 {
161 return total;
162 }
163
164 public long getTransferred()
165 {
166 return transferred;
167 }
168
169 public boolean isFinished()
170 {
171 return getRemaining() == 0;
172 }
173
174 public void setDownload(OpenNapDownload download)
175 {
176 this.download = download;
177 }
178
179 /***
180 * <p>Modifies: <code>file</code>
181 */
182 public synchronized void setFile(File file)
183 {
184 data.file = file;
185 }
186
187 /***
188 * Commits l bytes as transferred.
189 *
190 * <p>Modifies: <code>transferred</code>.
191 *
192 */
193 synchronized void commit(int l)
194 {
195 transferred += l;
196 }
197
198 /***
199 * Commits l bytes as transferred.
200 *
201 * <p>Modifies <code>transferred</code>, <code>offset</code>.
202 *
203 * @return the number of bytes that were accepted and should
204 * be written to file
205 */
206 synchronized int commitToFile(int l)
207 {
208 if (data.start + transferred + l > end) {
209 l = (int)(end - data.start - transferred);
210 }
211
212 transferred += l;
213
214 return l;
215 }
216
217 synchronized long getRemaining()
218 {
219 return end - data.start - transferred;
220 }
221
222 /***
223 * Appends <code>n</code> after this segment. Shortens this segment.
224 *
225 * <p>Modifies: <code>end</code>
226 */
227 synchronized void append(OpenNapSegment next)
228 {
229 if (next.data.start
230 < this.data.start + this.transferred - OpenNapSegmentManager.OVERLAP) {
231
232 throw new IllegalArgumentException("invalid start");
233 }
234
235
236 this.end = next.data.start + OpenNapSegmentManager.OVERLAP;
237
238 this.next = next;
239 next.previous = this;
240 }
241
242 /***
243 * Merges next into this segment by appending it.
244 */
245 synchronized void merge(OpenNapSegment next, int overlap)
246 {
247 this.end = next.getEnd();
248 this.transferred += next.getTransferred() - overlap;
249
250 this.next = next.getNext();
251 if (this.next != null) {
252 this.next.previous = this;
253 }
254
255 next.previous = null;
256 next.next = null;
257 }
258
259
260 /***
261 * <p>Modifies: <code>end</code>
262 */
263 synchronized OpenNapSegment split(int overlap)
264 {
265 long oldEnd = end;
266 long half = getRemaining() / 2;
267
268 if (half < MIN_SEGEMENT_SIZE) {
269 return null;
270 }
271
272
273 end = data.start + transferred + half;
274 OpenNapSegment s = new OpenNapSegment(end - overlap, oldEnd, getTotal());
275
276 s.next = this.next;
277 if (s.next != null) {
278 s.next.previous = s;
279 }
280 s.previous = this;
281 this.next = s;
282 return s;
283 }
284
285 synchronized void trim()
286 {
287 end = data.start + transferred;
288 }
289 }