Skip to content

Commit 4d763ac

Browse files
committed
Initial commit
0 parents  commit 4d763ac

File tree

8 files changed

+420
-0
lines changed

8 files changed

+420
-0
lines changed

.gitignore

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
target/
2+
!.mvn/wrapper/maven-wrapper.jar
3+
!**/src/main/**/target/
4+
!**/src/test/**/target/
5+
6+
### IntelliJ IDEA ###
7+
.idea/modules.xml
8+
.idea/jarRepositories.xml
9+
.idea/compiler.xml
10+
.idea/libraries/
11+
*.iws
12+
*.iml
13+
*.ipr
14+
15+
### Eclipse ###
16+
.apt_generated
17+
.classpath
18+
.factorypath
19+
.project
20+
.settings
21+
.springBeans
22+
.sts4-cache
23+
24+
### NetBeans ###
25+
/nbproject/private/
26+
/nbbuild/
27+
/dist/
28+
/nbdist/
29+
/.nb-gradle/
30+
build/
31+
!**/src/main/**/build/
32+
!**/src/test/**/build/
33+
34+
### VS Code ###
35+
.vscode/
36+
37+
### Mac OS ###
38+
.DS_Store
39+
/.idea/
40+
/out/

pom.xml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<groupId>com.robothaver</groupId>
8+
<artifactId>TorrentFileParser</artifactId>
9+
<version>1.0-SNAPSHOT</version>
10+
11+
<properties>
12+
<maven.compiler.source>17</maven.compiler.source>
13+
<maven.compiler.target>17</maven.compiler.target>
14+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15+
</properties>
16+
17+
</project>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.robothaver.torrentfileparser;
2+
3+
import com.robothaver.torrentfileparser.domain.Torrent;
4+
import com.robothaver.torrentfileparser.exception.MalformedTorrentFileException;
5+
6+
import java.io.IOException;
7+
import java.nio.file.Files;
8+
import java.nio.file.Path;
9+
import java.nio.file.Paths;
10+
import java.time.Duration;
11+
import java.time.Instant;
12+
13+
public class Main {
14+
public static void main(String[] args) {
15+
Path path = Paths.get("Path to torrent file");
16+
try {
17+
byte[] bytes = Files.readAllBytes(path);
18+
Instant start = Instant.now();
19+
20+
TorrentFileParser torrentParser = new TorrentFileParser(bytes);
21+
Torrent torrent = torrentParser.parseToTorrent();
22+
System.out.println(torrent.getName());
23+
24+
Instant finish = Instant.now();
25+
long timeElapsed = Duration.between(start, finish).toMillis();
26+
System.out.println("Parsing finished in " + timeElapsed + "ms");
27+
} catch (IOException | MalformedTorrentFileException e) {
28+
throw new RuntimeException(e);
29+
}
30+
}
31+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package com.robothaver.torrentfileparser;
2+
3+
import com.robothaver.torrentfileparser.domain.Torrent;
4+
import com.robothaver.torrentfileparser.domain.TorrentBuilder;
5+
import com.robothaver.torrentfileparser.exception.MalformedTorrentFileException;
6+
7+
import java.util.*;
8+
9+
public class TorrentFileParser {
10+
private final byte[] bytes;
11+
private int iterator = 0;
12+
private TorrentBuilder torrentBuilder = null;
13+
14+
public TorrentFileParser(byte[] bytes) {
15+
this.bytes = bytes;
16+
}
17+
18+
private Object parse() throws MalformedTorrentFileException {
19+
return switch (bytes[iterator]) {
20+
case 'i' -> parseInt();
21+
case 'l' -> parseList();
22+
case 'd' -> parseDict();
23+
default -> {
24+
if (isInt(bytes[iterator])) {
25+
yield parseString();
26+
} else {
27+
throw new MalformedTorrentFileException("Illegal character " + (char) bytes[iterator] + " at " + iterator);
28+
}
29+
}
30+
};
31+
}
32+
33+
public Torrent parseToTorrent() throws MalformedTorrentFileException {
34+
torrentBuilder = new TorrentBuilder();
35+
parse();
36+
iterator = 0;
37+
return torrentBuilder.getTorrent();
38+
}
39+
40+
public Map<String, Object> parseToMap() throws MalformedTorrentFileException {
41+
return (Map<String, Object>) parse();
42+
}
43+
44+
private boolean isInt(byte currentByte) {
45+
return (byte) '0' <= currentByte && currentByte <= (byte) '9';
46+
}
47+
48+
private List<Object> parseList() throws MalformedTorrentFileException {
49+
iterator++; // Skipping the l
50+
List<Object> list = new ArrayList<>();
51+
while (bytes[iterator] != 'e') {
52+
list.add(parse());
53+
}
54+
iterator++; // Skipping closing e
55+
return list;
56+
}
57+
58+
private Map<String, Object> parseDict() throws MalformedTorrentFileException {
59+
iterator++; // Skipping the d
60+
Map<String, Object> map = new HashMap<>();
61+
String key = null;
62+
while (bytes[iterator] != 'e') {
63+
Object parseValue = parse();
64+
if (key == null) {
65+
key = String.valueOf(parseValue);
66+
} else {
67+
map.put(key, parseValue);
68+
if (torrentBuilder != null) {
69+
torrentBuilder.processKeyValue(key, parseValue);
70+
}
71+
key = null;
72+
}
73+
}
74+
iterator++; // Skipping closing e
75+
return map;
76+
}
77+
78+
private String parseString() throws MalformedTorrentFileException {
79+
int startIndex = iterator;
80+
int colonIndex = 0;
81+
while (iterator < bytes.length) {
82+
if (bytes[iterator] == (byte) ':') {
83+
colonIndex = iterator;
84+
break;
85+
} else {
86+
iterator++;
87+
}
88+
}
89+
int length;
90+
try {
91+
length = Integer.parseInt(new String(Arrays.copyOfRange(bytes, startIndex, colonIndex)));
92+
} catch (NumberFormatException e) {
93+
throw new MalformedTorrentFileException();
94+
}
95+
96+
String value = new String(Arrays.copyOfRange(bytes, colonIndex + 1, colonIndex + length + 1));
97+
iterator += length + 1;
98+
return value;
99+
}
100+
101+
private Long parseInt() {
102+
int startIndex = iterator + 1;
103+
while (bytes[iterator] != 'e') {
104+
iterator++;
105+
}
106+
byte[] valueBytes = Arrays.copyOfRange(bytes, startIndex, iterator);
107+
iterator++; // Skipping closing e
108+
return Long.parseLong(new String(valueBytes));
109+
}
110+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package com.robothaver.torrentfileparser.domain;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.Map;
6+
7+
public class Torrent {
8+
private String announce;
9+
private String name;
10+
private Long pieceLength;
11+
private boolean isSingleFile = true;
12+
private boolean isPrivate;
13+
private Long totalLength = 0L;
14+
private String pieces;
15+
private final List<TorrentFile> files = new ArrayList<>();
16+
17+
// Optional fields
18+
private List<List<String>> announceList = null;
19+
private String creator = null;
20+
private Long creationDate = null;
21+
private String source = null;
22+
private String comment = null;
23+
private String encoding = null;
24+
private Map<String, Object> azureusProperties = null;
25+
26+
public String getAnnounce() {
27+
return announce;
28+
}
29+
30+
public void setAnnounce(String announce) {
31+
this.announce = announce;
32+
}
33+
34+
public String getName() {
35+
return name;
36+
}
37+
38+
public void setName(String name) {
39+
this.name = name;
40+
}
41+
42+
public Long getPieceLength() {
43+
return pieceLength;
44+
}
45+
46+
public void setPieceLength(Long pieceLength) {
47+
this.pieceLength = pieceLength;
48+
}
49+
50+
public boolean isSingleFile() {
51+
return isSingleFile;
52+
}
53+
54+
public void setSingleFile(boolean singleFile) {
55+
isSingleFile = singleFile;
56+
}
57+
58+
public boolean isPrivate() {
59+
return isPrivate;
60+
}
61+
62+
public void setPrivate(boolean aPrivate) {
63+
isPrivate = aPrivate;
64+
}
65+
66+
public Long getTotalLength() {
67+
return totalLength;
68+
}
69+
70+
public void setTotalLength(Long totalLength) {
71+
this.totalLength = totalLength;
72+
}
73+
74+
public String getPieces() {
75+
return pieces;
76+
}
77+
78+
public void setPieces(String pieces) {
79+
this.pieces = pieces;
80+
}
81+
82+
public List<TorrentFile> getFiles() {
83+
return files;
84+
}
85+
86+
public List<List<String>> getAnnounceList() {
87+
return announceList;
88+
}
89+
90+
public void setAnnounceList(List<List<String>> announceList) {
91+
this.announceList = announceList;
92+
}
93+
94+
public String getCreator() {
95+
return creator;
96+
}
97+
98+
public void setCreator(String creator) {
99+
this.creator = creator;
100+
}
101+
102+
public Long getCreationDate() {
103+
return creationDate;
104+
}
105+
106+
public void setCreationDate(Long creationDate) {
107+
this.creationDate = creationDate;
108+
}
109+
110+
public String getSource() {
111+
return source;
112+
}
113+
114+
public void setSource(String source) {
115+
this.source = source;
116+
}
117+
118+
public String getComment() {
119+
return comment;
120+
}
121+
122+
public void setComment(String comment) {
123+
this.comment = comment;
124+
}
125+
126+
public String getEncoding() {
127+
return encoding;
128+
}
129+
130+
public void setEncoding(String encoding) {
131+
this.encoding = encoding;
132+
}
133+
134+
public Map<String, Object> getAzureusProperties() {
135+
return azureusProperties;
136+
}
137+
138+
public void setAzureusProperties(Map<String, Object> azureusProperties) {
139+
this.azureusProperties = azureusProperties;
140+
}
141+
142+
@Override
143+
public String toString() {
144+
return "Torrent{" +
145+
"announce='" + announce + '\'' +
146+
", name='" + name + '\'' +
147+
", pieceLength=" + pieceLength +
148+
", isSingleFile=" + isSingleFile +
149+
", isPrivate=" + isPrivate +
150+
", totalLength=" + totalLength +
151+
", files=" + files +
152+
", announceList=" + announceList +
153+
", creator='" + creator + '\'' +
154+
", creationDate=" + creationDate +
155+
", source='" + source + '\'' +
156+
", comment='" + comment + '\'' +
157+
", encoding='" + encoding + '\'' +
158+
", azureusProperties=" + azureusProperties +
159+
'}';
160+
}
161+
}

0 commit comments

Comments
 (0)