Skip to content

Commit a6e3e7a

Browse files
Merge pull request #1 from sator-imaging/v2.0
V2.0
2 parents 074b065 + ea1ae37 commit a6e3e7a

File tree

5 files changed

+93
-106
lines changed

5 files changed

+93
-106
lines changed

README.md

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
TAR Archiver for C# / .NET
2-
==========================
1+
TAR Archiver for .NET / Unity
2+
=============================
33

4-
Minimal C# implementation for creating TAR archive (.tar/.tar.gz/.tgz)
4+
Minimal C# implementation for creating TAR archive. (.tar/.tar.gz/.tgz)
55

66
This library is built based on the codes from [SharpCompress](https://github.com/adamhathcock/sharpcompress) library with:
77

88
- `unsafe` Context Removal
99
- `Span<T> = stackalloc T` Remain Untouched &nbsp; <small>* Require C# 7.3 or Later</small>
10-
- Nullables Removal
11-
- `System.Buffers` Dependency Removal
1210
- Unity Ready without additional DLL Installation
1311
- Support for Unity Package Manager
12+
- Unity 2021 LTS or Later
1413

1514

1615

@@ -29,7 +28,6 @@ Not complicated. The only feature is creating TAR archive from `string`, `byte[]
2928
Thanks to `System.IO.Compression` library, you can also create `.tar.gz` (`.tgz`) archive on the fly.
3029

3130

32-
3331
```csharp
3432
using SatorImaging.TarArchiver;
3533
using System;
@@ -56,6 +54,9 @@ public class Sample
5654
tar.Flush(); // done in TarStream.Dispose() anyway
5755
}
5856

57+
// NOTE: To use MemoryStream instead of FileStream for underlying base stream
58+
// of GZipStream, it must be CLOSED before calling MemoryStream.ToArray().
59+
// GZipStream.Flush() won't write anything, close it first.
5960
}
6061
}
6162
```
@@ -161,19 +162,6 @@ Use the following git URL to import this library using Unity Package Manager (UP
161162
- v1.0.0: `https://github.com/sator-imaging/CSharp-TarArchiver.git#v1.0.0`
162163

163164

164-
Note that `src/Sample.cs` will add menu to Unity Editor that shows dialog for exporting test file.
165+
Note that `src/Tests.cs` will add menu to Unity Editor that shows dialog for exporting test file.
165166

166167
![](https://dl.dropbox.com/s/5qkzw1j4a0ony5a/CSharp-TarArchiver.png)
167-
168-
169-
170-
# Changelog
171-
172-
- Nullables removed.
173-
174-
- Added rethrow code original one doesn't.
175-
176-
- `stackalloc`: removed
177-
- `Span<T> = stackalloc T`: untouched
178-
- `ArrayPool<Byte>`: removed
179-
- `BinaryPrimitives.WriteInt64BigEndian`: polyfilled

package.json

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1-
{
2-
"name": "com.sator-imaging.tar-archiver",
3-
"displayName": "TAR Archiver for C# / .NET",
4-
"version": "1.0.2",
5-
"unity": "2021.3",
6-
"description": "Minimal C# implementation for creating TAR (.tar) archive file.",
7-
8-
"author": {
9-
"name": "Sator Imaging",
10-
"url": "https://www.sator-imaging.com/"
11-
},
12-
13-
"category": "tar",
14-
"keywords": [
15-
"tar",
16-
"archive"
17-
]
18-
19-
}
1+
{
2+
"name": "com.sator-imaging.tar-archiver",
3+
"displayName": "TAR Archiver for .NET / Unity",
4+
"version": "2.0.0",
5+
"unity": "2021.3",
6+
"description": "Minimal C# implementation for creating TAR (.tar) archive file.",
7+
"author": {
8+
"name": "Sator Imaging",
9+
"url": "https://x.com/sator_imaging"
10+
},
11+
"category": "tar",
12+
"keywords": [
13+
"unity",
14+
"tar",
15+
"archive"
16+
],
17+
"url": "https://github.com/sator-imaging/CSharp-TarArchiver.git",
18+
"documentationUrl": "https://github.com/sator-imaging/CSharp-TarArchiver",
19+
"changelogUrl": "https://github.com/sator-imaging/CSharp-TarArchiver",
20+
"licensesUrl": "https://www.sator-imaging.com/"
21+
}

src/Sample.cs

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,49 @@
1-
using SatorImaging.TarArchiver;
1+
#if DEBUG
2+
23
using System;
34
using System.IO;
45
using System.IO.Compression;
56

7+
#nullable enable
68

7-
public class Sample
9+
namespace SatorImaging.TarArchiver
810
{
9-
10-
11-
#if UNITY_EDITOR
12-
[UnityEditor.MenuItem(nameof(SatorImaging.TarArchiver) + "/Create Test File")]
13-
static void CreateTestFile()
11+
public class Tests
1412
{
15-
var path = UnityEditor.EditorUtility.SaveFilePanel(nameof(SatorImaging.TarArchiver), "", "TestFile", @"tar.gz");
16-
Main(new string[] { path });
17-
}
18-
#endif
19-
13+
#if UNITY_EDITOR
14+
[UnityEditor.MenuItem("TEST/" + nameof(TarArchiver) + "/Create Test File...")]
15+
static void Create_Test_File()
16+
{
17+
var path = UnityEditor.EditorUtility.SaveFilePanel(
18+
nameof(TarArchiver), string.Empty, "TestFile", @"tar.gz");
2019

21-
static void Main(string[] args)
22-
{
23-
if (args.Length == 0 || string.IsNullOrWhiteSpace(args[0]))
24-
throw new ArgumentNullException(nameof(args));
20+
CreateTestFile(new string[] { path });
21+
UnityEditor.EditorUtility.RevealInFinder(path);
22+
}
23+
#endif
2524

26-
// open TAR stream and export as a .tar.gz format
27-
using (var targz = new FileStream(args[0], FileMode.Create, FileAccess.Write))
28-
using (var gzip = new GZipStream(targz, CompressionLevel.Optimal))
29-
using (var tar = new TarStream(gzip))
25+
static void CreateTestFile(string[] args)
3026
{
31-
// writing data
32-
tar.Write(@"path/to/the/file.txt", @"Hello, World.");
33-
tar.Write(@"path/with/multibyte/文字列.txt", @"ひらがなカタカナ漢字");
34-
tar.Write(@"yet-another-folder/byteArray.txt", new byte[] { 84, 65, 82, 13, 10 }); //TAR[CR][LF]
35-
tar.Write(@"root.txt", "");
36-
tar.Flush(); // done in TarStream.Dispose() anyway
27+
if (args.Length == 0 || string.IsNullOrWhiteSpace(args[0]))
28+
throw new ArgumentNullException(nameof(args));
29+
30+
// open TAR stream and export as a .tar.gz format
31+
using (var targz = new FileStream(args[0], FileMode.Create, FileAccess.Write))
32+
using (var gzip = new GZipStream(targz, CompressionLevel.Optimal))
33+
using (var tar = new TarStream(gzip))
34+
{
35+
// writing data
36+
tar.Write(@"path/to/the/file.txt", @"Hello, World.");
37+
tar.Write(@"path/with/multibyte/文字列.txt", @"ひらがなカタカナ漢字");
38+
tar.Write(@"yet-another-folder/byteArray.txt", new byte[] { 84, 65, 82, 13, 10 }); //TAR[CR][LF]
39+
tar.Write(@"root.txt", "");
40+
tar.Flush(); // done in TarStream.Dispose() anyway
41+
}
42+
43+
// NOTE: To use MemoryStream instead of FileStream for underlying base stream
44+
// of GZipStream, it must be CLOSED before calling MemoryStream.ToArray().
45+
// GZipStream.Flush() won't write anything, close it first.
3746
}
38-
3947
}
4048
}
49+
#endif

src/TarHeader.cs

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
using System.IO;
33
using System.Text;
44

5+
#nullable enable
56

67
namespace SatorImaging.TarArchiver
78
{
89
public class TarHeader
910
{
1011
const int BLOCK_SIZE = 512;
11-
static DateTime EPOCH = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
12+
static readonly DateTime EPOCH = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
13+
static readonly Encoding ArchiveEncoding = Encoding.UTF8;
1214

1315
enum EEntryType : byte
1416
{
@@ -27,18 +29,15 @@ enum EEntryType : byte
2729
GlobalExtendedHeader = (byte)'g'
2830
}
2931

30-
EEntryType EntryType;
31-
Encoding ArchiveEncoding = Encoding.UTF8;
3232

33-
34-
internal string Name { get; set; }
33+
internal string Name { get; set; } = string.Empty;
3534
internal long Size { get; set; }
3635
internal DateTime LastModifiedTime { get; set; }
3736

3837

3938
internal void Write(Stream output)
4039
{
41-
byte[] buffer = new byte[BLOCK_SIZE];
40+
var buffer = (stackalloc byte[BLOCK_SIZE]);
4241

4342
WriteOctalBytes(511, buffer, 100, 8); // file mode
4443
WriteOctalBytes(0, buffer, 108, 8); // owner ID
@@ -59,21 +58,21 @@ internal void Write(Stream output)
5958
WriteOctalBytes(Size, buffer, 124, 12);
6059
var time = (long)(LastModifiedTime.ToUniversalTime() - EPOCH).TotalSeconds;
6160
WriteOctalBytes(time, buffer, 136, 12);
62-
buffer[156] = (byte)EntryType;
61+
buffer[156] = (byte)(default(EEntryType));
6362

6463
if (Size >= 0x1FFFFFFFF)
6564
{
6665
Span<byte> bytes12 = stackalloc byte[12];
6766
BinaryPrimitives_WriteInt64BigEndian(bytes12.Slice(4), Size);
6867
bytes12[0] |= 0x80;
69-
bytes12.CopyTo(buffer.AsSpan(124));
68+
bytes12.CopyTo(buffer.Slice(124));
7069
}
7170
}
7271

7372
int crc = RecalculateChecksum(buffer);
7473
WriteOctalBytes(crc, buffer, 148, 8);
7574

76-
output.Write(buffer, 0, buffer.Length);
75+
output.Write(buffer);
7776

7877
if (nameByteCount > 100)
7978
{
@@ -91,10 +90,8 @@ internal void Write(Stream output)
9190
}
9291

9392

94-
9593
#region //////// Utility ////////
9694

97-
9895
static void BinaryPrimitives_WriteInt64BigEndian(Span<byte> destination, long value)
9996
{
10097
var bytes = BitConverter.GetBytes(value).AsSpan<byte>();
@@ -106,7 +103,7 @@ static void BinaryPrimitives_WriteInt64BigEndian(Span<byte> destination, long va
106103
}
107104

108105

109-
static void WriteOctalBytes(long value, byte[] buffer, int offset, int length)
106+
static void WriteOctalBytes(long value, Span<byte> buffer, int offset, int length)
110107
{
111108
var val = Convert.ToString(value, 8);
112109
var shift = length - val.Length - 1;
@@ -128,7 +125,7 @@ static void WriteStringBytes(ReadOnlySpan<byte> name, Span<byte> buffer, int len
128125
buffer.Slice(i, length - i).Clear();
129126
}
130127

131-
static void WriteStringBytes(string name, byte[] buffer, int offset, int length)
128+
static void WriteStringBytes(string name, Span<byte> buffer, int offset, int length)
132129
{
133130
int i;
134131

@@ -155,7 +152,7 @@ void WriteLongFilenameHeader(Stream output)
155152
{
156153
numPaddingBytes = BLOCK_SIZE;
157154
}
158-
output.Write(new byte[numPaddingBytes]);
155+
output.Write(stackalloc byte[numPaddingBytes]);
159156
}
160157

161158

@@ -171,10 +168,10 @@ void WriteLongFilenameHeader(Stream output)
171168
(byte)' ',
172169
};
173170

174-
static int RecalculateChecksum(byte[] buf)
171+
static int RecalculateChecksum(Span<byte> buf)
175172
{
176173
// Set default value for checksum. That is 8 spaces.
177-
eightSpaces.CopyTo(buf, 148);
174+
eightSpaces.CopyTo(buf.Slice(148));
178175

179176
// Calculate checksum
180177
var headerChecksum = 0;
@@ -185,10 +182,6 @@ static int RecalculateChecksum(byte[] buf)
185182
return headerChecksum;
186183
}
187184

188-
189185
#endregion
190-
191-
192-
193186
}
194187
}

0 commit comments

Comments
 (0)