Skip to content

Instantly share code, notes, and snippets.

@k0t0vich
Created August 22, 2014 10:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save k0t0vich/6a6cc85f8799d0f4560e to your computer and use it in GitHub Desktop.
Save k0t0vich/6a6cc85f8799d0f4560e to your computer and use it in GitHub Desktop.
package ru.riot.utils.net.tar {
import flash.display.Shape;
import flash.events.Event;
import flash.utils.IDataInput;
[Event(name="fileParsed", type="ru.riot.utils.net.tar.TarParserEvent")]
[Event(name="archiveParsed", type="ru.riot.utils.net.tar.TarParserEvent")]
public class StreamTarParser extends TarParser {
private static const BLOCK_SIZE: int = 512;
private static const frameListener:Shape = new Shape();
public static var checkRecursive:Boolean = false;
private var _operation: Function;
private var _needSize: uint;
private var _currentFile: TarFile;
// флаг ограничения реку
private var recursiveCounter:int = 0;
private var maxRecursiveCounter:int = 100;
public function StreamTarParser(bytes: IDataInput) {
super(bytes);
_needSize = BLOCK_SIZE;
operation = headerOperation;
}
override public function read():void {
if (_data.bytesAvailable >= _needSize) {
_operation.call();
}
}
private function recursiveRead():void {
if (checkRecursive) {
recursiveCounter++;
if (recursiveCounter > maxRecursiveCounter){
recursiveCounter = 0;
frameListener.addEventListener(Event.ENTER_FRAME, nextFrameRead);
} else {
read();
}
} else {
read();
}
}
protected function nextFrameRead(event:Event):void {
frameListener.removeEventListener(Event.ENTER_FRAME, nextFrameRead);
read();
}
private function headerOperation():void {
_currentFile = readFileHeader();
if (_currentFile){
// если не каталог или пустой файл, читаем контент
if (_currentFile.size > 0) {
_needSize = _currentFile.size;
operation = contentOperation;
} else {
// иначе читаем следующий заголовок
recursiveRead();
}
} else {
_needSize = BLOCK_SIZE * 2 - 100;
operation = endArchiveOperation;
}
}
private function contentOperation():void {
_data.readBytes(_currentFile.data, 0, _currentFile.size);
var pad:uint = getPad(_currentFile.size);
if (pad > 0 ) {
_needSize = pad;
operation = padOperation;
} else {
operation = endFileOperation;
}
}
private function padOperation():void {
skipBytes(_needSize);
operation = endFileOperation;
}
private function endArchiveOperation():void {
skipBytes(_needSize);
var event:TarParserEvent = new TarParserEvent(TarParserEvent.ARCHIVE_PARSED);
dispatchEvent(event);
}
private function endFileOperation():void {
var event:TarParserEvent = new TarParserEvent(TarParserEvent.FILE_PARSED, _currentFile);
dispatchEvent(event);
_needSize = BLOCK_SIZE;
operation = headerOperation;
}
private function set operation(value:Function):void {
if( _operation !== value){
_operation = value;
recursiveRead();
}
}
}
}
package ru.riot.utils.net.tar {
import flash.utils.ByteArray;
public class TarFile {
public var name : String;
public var size : uint;
public var data : ByteArray;
public function TarFile(name:String) {
super();
this.name = name;
data = new ByteArray();
}
}
}
package ru.riot.utils.net.tar {
import flash.events.EventDispatcher;
import flash.utils.ByteArray;
import flash.utils.IDataInput;
public class TarParser extends EventDispatcher {
protected var _data: IDataInput;
private var _files:Array;
public function get files():Array {
return _files;
}
public function TarParser(bytes: IDataInput) {
super();
this._data = bytes;
}
public function read():void {
_files = new Array();
while (true) {
var e: TarFile = readFileHeader();
if (e == null)
break;
var size: uint = e.size;
if (e.size > 0) {
_data.readBytes(e.data, 0, size);
_files.push(e);
var pad:uint = getPad(size);
//trace ("size", size,"pad", pad);
skipBytes(pad);
}
}
}
/**
* Parse Header and create new tar file
* struct header_posix_ustar {
char name[100];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char checksum[8];
char typeflag[1];
char linkname[100];
char magic[6];
char version[2];
char uname[32];
char gname[32];
char devmajor[8];
char devminor[8];
char prefix[155];
char pad[12];
};
*/
protected function readFileHeader():TarFile {
var fname: String = _data.readUTFBytes(100);
if (!fname || fname.length == 0) {
return null;
}
var tarFile: TarFile = new TarFile(fname);
// нам не нужны никакие данные, кроме имени и размера - скипаем
skipBytes( 8 + 8 + 8);
tarFile.size = parseOctal(12);
skipBytes(8 + 4 + 8 + 1 + 100 + 8 + 32 + 32 + 8 + 8 + 167);
return tarFile;
}
protected function getPad(size: uint):uint {
return Math.ceil(size / 512) * 512 - size;
}
protected function skipBytes(size: uint):void {
var bytes: ByteArray = new ByteArray();
_data.readBytes(bytes, 0, size);
bytes.clear();
}
protected function parseOctal(size: uint = 8):int {
var bytes: ByteArray = new ByteArray();
_data.readBytes(bytes, 0, size);
var num: int = 0;
for (var p: uint = 0; p < size; p++) {
var c: int = bytes.readByte();
if (c == 0)
break;
if (c == 32)
continue;
if (c < 48 || c > 55) {
return 0;
//throw new Error("Invalid octal char");
}
num = (num * 8) + (c - 48);
}
bytes.clear();
return num;
}
}
}
package ru.riot.utils.net.tar {
import flash.events.Event;
public class TarParserEvent extends Event {
public static const FILE_PARSED: String = "fileParsed";
public static const ARCHIVE_PARSED: String = "archiveParsed";
private var _file: TarFile;
public function TarParserEvent(type: String, file: TarFile = null) {
super(type);
_file = file;
}
override public function clone():Event {
return new TarParserEvent(type, _file);
}
public function get file():TarFile {
return _file;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment