C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[13]: 添え字がInt64(long)の可変長リスト


(過去ログ 27 を表示中)

[トピック内 14 記事 (1 - 14 表示)]  << 0 >>

■12278 / inTopicNo.1)  添え字がInt64(long)の可変長リストが欲しい
  
□投稿者/ Hirotow (114回)-(2008/01/07(Mon) 14:22:17)

分類:[.NET 全般] 

現在製作中のバイナリ編集コントロールなのですが、
バッファにList<byte>を使っているため、添え字として符号つき32ビットまでしか渡すことができません。
しかしながら、System.IO名前空間のメソッドして64ビットまでのファイルをサポートしており、
また今回のツールで編集対象としているMP4のボックスごとの最大サイズは符号なし32ビットなので、
仮にそういったデータが渡された場合正常に表示することができません。
そういうことでバッファに符号つき64ビットの添え字をサポートしたものを使用したいのですが、適当なクラスが.NETに見つかりません。
またもっとも適当そうなMemoryStreamを使った場合性能面で不安があります。
こういった場合どうすればいいかご教示いただけると幸いです。

追記:
スクロールバーの範囲はMaximumとLeageChangeのセットで指定するものなんですね。
引用返信 編集キー/
■12280 / inTopicNo.2)  Re[1]: 添え字がInt64(long)の可変長リストが欲しい
□投稿者/ なちゃ (89回)-(2008/01/07(Mon) 14:38:17)
その前にメモリ空間が足りなくなりませんか?多くの環境では。
リニアに確保するのはさらに制約が厳しいでしょう。

作業部分をキャッシュのようにメモリ上に展開するような工夫が必須な気がします。

引用返信 編集キー/
■12283 / inTopicNo.3)  Re[2]: 添え字がInt64(long)の可変長リストが欲しい
□投稿者/ Hirotow (116回)-(2008/01/07(Mon) 15:52:23)
No12280 (なちゃ さん) に返信
> その前にメモリ空間が足りなくなりませんか?多くの環境では。
> リニアに確保するのはさらに制約が厳しいでしょう。
>
> 作業部分をキャッシュのようにメモリ上に展開するような工夫が必須な気がします。
>
確かに容量によってはOutOfMemoryExceptionが発生してました。

内部バッファを読み込み+シーク可能なストリームにし、外部からのアクセスもストリームにしました。
また大容量(100MB以上)のファイルの場合はFileStreamをそのまま、小容量の場合はMemoryStreamにコピーしてからセットすることでGB単位のファイルでも問題なく読み込めるようになりました。

現在の最新版:
http://mui-style.net/data/BinaryEditor_Mikansei_D.zip

引用返信 編集キー/
■12284 / inTopicNo.4)  Re[3]: 添え字がInt64(long)の可変長リストが欲しい
□投稿者/ Hirotow (117回)-(2008/01/07(Mon) 15:55:10)
あとバッファ関連の問題はどうやってInsert編集を実装するか…
書くたびにそれ以降のバイトをずらしてたら埒が明かないし。
ある程度まとめてとってから読み飛ばし位置を別に保持するとかすべきかな…
引用返信 編集キー/
■12305 / inTopicNo.5)  Re[4]: 添え字がInt64(long)の可変長リストが欲しい
□投稿者/ れい (354回)-(2008/01/07(Mon) 20:58:25)
No12284 (Hirotow さん) に返信
> あとバッファ関連の問題はどうやってInsert編集を実装するか…
> 書くたびにそれ以降のバイトをずらしてたら埒が明かないし。
> ある程度まとめてとってから読み飛ばし位置を別に保持するとかすべきかな…

移動量を保存しておくのがいいでしょうね。

大容量の場合もそうですが、
ページの概念を導入するのが一般的かと思います。
1K単位とか、1M単位でページとし、
編集・未編集のフラグと、本来の位置から何バイトずれているのか記録します。

ずれの量を起動しておけば挿入・削除に対応できます。

引用返信 編集キー/
■12309 / inTopicNo.6)  Re[5]: 添え字がInt64(long)の可変長リストが欲しい
□投稿者/ Hirotow (120回)-(2008/01/07(Mon) 21:31:53)
いまいち理解できないのでもう少し具体的に教示してもらえると助かります。
No12305 (れい さん) に返信
> ■No12284 (Hirotow さん) に返信
>>あとバッファ関連の問題はどうやってInsert編集を実装するか…
>>書くたびにそれ以降のバイトをずらしてたら埒が明かないし。
>>ある程度まとめてとってから読み飛ばし位置を別に保持するとかすべきかな…
>
> 移動量を保存しておくのがいいでしょうね。
>
> 大容量の場合もそうですが、
> ページの概念を導入するのが一般的かと思います。
> 1K単位とか、1M単位でページとし、
> 編集・未編集のフラグと、本来の位置から何バイトずれているのか記録します。
>
> ずれの量を起動しておけば挿入・削除に対応できます。
>
引用返信 編集キー/
■12317 / inTopicNo.7)  Re[6]: 添え字がInt64(long)の可変長リストが欲しい
□投稿者/ れい (355回)-(2008/01/07(Mon) 23:37:27)
No12309 (Hirotow さん) に返信
> いまいち理解できないのでもう少し具体的に教示してもらえると助かります。

むむ。
私の日本語能力だと「もう少し」具体的に書くのは難しいです。
説明が長大になるか、コードになってしまいます。

あんまり詳細に説明するのもめんどくさいですし
Hirotowさんのオリジナリティを損なう可能性がありますので、
適当に言いますが、

・元ファイル上での開始位置と長さ
・編集済みフラグ
・実際のデータが保存される可変長のByte配列

の一連の情報をまとめてページとし、
ページ単位でデータを管理するようにすると
挿入・削除や大きいファイルの編集も比較的簡単にできます。

エディタは、表示されているページと編集済みページリストを保持します。
カーソルが移動してあるページが表示されなくなった場合、
未編集であれば保持する必要がなくなります。
編集済みであれば編集済みページリストに追加しておく必要があります。

カーソルが移動して、新たにページを表示する場合、
編集済みページリストにあればそれを使えばいいですし、
なければファイルから適当な量だけ読み込んでページを作成し、それを使います。

挿入・削除操作が行われた場合は該当ページにだけ行えばよいので、
全データをシフトする必要もなくなります。
たくさん行われた時はページの分割・統合が必要になります。

編集されたデータをファイルに保存する時は
編集済みページリストと元ファイルを突き合わせれば
編集後のファイルを作成することができます。

このように、元ファイルの一部分だけをいじる方法は
大きいファイルではよくやる手です。
引用返信 編集キー/
■12327 / inTopicNo.8)  Re[7]: 添え字がInt64(long)の可変長リストが欲しい
□投稿者/ Hirotow (124回)-(2008/01/08(Tue) 01:13:30)
かなり作り直さないといけなさそうですが、がんばってやってみます。

No12317 (れい さん) に返信
> ■No12309 (Hirotow さん) に返信
>>いまいち理解できないのでもう少し具体的に教示してもらえると助かります。
>
> むむ。
> 私の日本語能力だと「もう少し」具体的に書くのは難しいです。
> 説明が長大になるか、コードになってしまいます。
>
> あんまり詳細に説明するのもめんどくさいですし
> Hirotowさんのオリジナリティを損なう可能性がありますので、
> 適当に言いますが、
>
> ・元ファイル上での開始位置と長さ
> ・編集済みフラグ
> ・実際のデータが保存される可変長のByte配列
>
> の一連の情報をまとめてページとし、
> ページ単位でデータを管理するようにすると
> 挿入・削除や大きいファイルの編集も比較的簡単にできます。
>
> エディタは、表示されているページと編集済みページリストを保持します。
> カーソルが移動してあるページが表示されなくなった場合、
> 未編集であれば保持する必要がなくなります。
> 編集済みであれば編集済みページリストに追加しておく必要があります。
>
> カーソルが移動して、新たにページを表示する場合、
> 編集済みページリストにあればそれを使えばいいですし、
> なければファイルから適当な量だけ読み込んでページを作成し、それを使います。
>
> 挿入・削除操作が行われた場合は該当ページにだけ行えばよいので、
> 全データをシフトする必要もなくなります。
> たくさん行われた時はページの分割・統合が必要になります。
>
> 編集されたデータをファイルに保存する時は
> 編集済みページリストと元ファイルを突き合わせれば
> 編集後のファイルを作成することができます。
>
> このように、元ファイルの一部分だけをいじる方法は
> 大きいファイルではよくやる手です。
引用返信 編集キー/
■12328 / inTopicNo.9)  Re[8]: 添え字がInt64(long)の可変長リストが欲しい
□投稿者/ れい (356回)-(2008/01/08(Tue) 01:50:52)
No12327 (Hirotow さん) に返信
> かなり作り直さないといけなさそうですが、がんばってやってみます。

量の見積もりは最初からきちんしておかないといけないということですね。

引用返信 編集キー/
■12349 / inTopicNo.10)  Re[9]: 添え字がInt64(long)の可変長リストが欲しい
□投稿者/ Hirotow (125回)-(2008/01/08(Tue) 14:06:01)
つまり、入力のStreamを複数のブロックに分割して、編集時は直接書き戻さずに該当するバッファに書く。
表示時はもし編集された領域であればバッファから読み込む。
ということだとして、挿入や削除がある場合バッファとStreamでアドレスの齟齬が生じてしまいますよね。
こういう場合はどうやってアドレスの同期をとればいいのでしょうか。
小一時間考えたのですが考えるだけ混乱します。
あと参考になるサイトとかあったら教えてください。

No12328 (れい さん) に返信
> ■No12327 (Hirotow さん) に返信
>>かなり作り直さないといけなさそうですが、がんばってやってみます。
>
> 量の見積もりは最初からきちんしておかないといけないということですね。
>
引用返信 編集キー/
■12352 / inTopicNo.11)  Re[10]: 添え字がInt64(long)の可変長リストが欲しい
□投稿者/ れい (357回)-(2008/01/08(Tue) 14:30:42)
No12349 (Hirotow さん) に返信
> つまり、入力のStreamを複数のブロックに分割して、編集時は直接書き戻さずに該当するバッファに書く。
> 表示時はもし編集された領域であればバッファから読み込む。
> ということだとして、挿入や削除がある場合バッファとStreamでアドレスの齟齬が生じてしまいますよね。
> こういう場合はどうやってアドレスの同期をとればいいのでしょうか。

んー
齟齬を無くすためにページの概念を導入して、各ページに
・元ファイル上での開始位置と長さ
・編集済みフラグ
・実際のデータが保存される可変長のByte配列
のデータを持たせるのです。

各ページにこれだけの情報を持っていれば、
データを読込・表示・編集・保存が十分にできるはずですよ。
引用返信 編集キー/
■12372 / inTopicNo.12)  Re[11]: 添え字がInt64(long)の可変長リストが欲しい
□投稿者/ Hirotow (126回)-(2008/01/08(Tue) 18:39:18)
<s>こうですか? わかりません><</s>こんな感じですかね?
うまく動く自信がないorz

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace BinaryEditor
{
	class BinaryBuffer
	{
		const int PAGE_LEN = 0x00002000;

		Stream stream;

		public Stream Stream
		{
			get { return stream; }
		}

		LinkedList<BinaryPage> pages;

		bool bReadOnly;
		public bool ReadOnly
		{
			get { return bReadOnly; }
			set { bReadOnly = value; }
		}

		long Length
		{
			get
			{
				long len = 0;
				foreach (BinaryPage p in pages) {
					len += p.Length;
				}
				return len;
			}
		}

		public BinaryBuffer()
		{
			stream = new MemoryStream();
		}

		public BinaryBuffer(Stream s)
		{
			if (!s.CanRead || !s.CanSeek) {
				throw new ArgumentException(
					"読み込みおよびシークの可能なストリームのみでBinaryBufferを初期化できます。");
			}
			if (!s.CanWrite) {
				bReadOnly = true;
			}
			stream = s;

			//ページの論理情報を作成
			pages = new LinkedList<BinaryPage>();
			for (long i = 0; i < s.Length / PAGE_LEN - 1; i++) {
				pages.AddLast(new BinaryPage(i * PAGE_LEN, PAGE_LEN));
			}
			pages.AddLast(new BinaryPage((s.Length / PAGE_LEN) * PAGE_LEN,
				(int)((s.Length / PAGE_LEN + 1) * PAGE_LEN - s.Length)));
		}

		private BinaryPage GetPageByPos(long pos)
		{
			BinaryPage page = null;
			foreach (BinaryPage p in pages) {
				if (p.Position < pos) {
					page = p;
					break;
				}
			}
			return page;
		}

		private BinaryPage GetPageByPos(long pos, out IEnumerator<BinaryPage> ie)
		{
			ie = pages.GetEnumerator();
			BinaryPage p = null, page = null;
			while (ie.MoveNext()) {
				p = ie.Current;
				if (p.Position < pos) {
					page = p;
					break;
				}
			}
			return page;
		}

		public byte GetByteAt(long pos)
		{
			BinaryPage page = GetPageByPos(pos);
			if (page.Modified) {
				return page.Buffer[(int)(pos - page.Position)];
			} else {
				stream.Seek(pos - page.Position + page.SourcePosition, SeekOrigin.Begin);
				return (byte)stream.ReadByte();
			}
		}

		public byte[] GetBytesAt(long pos, int len)
		{
			byte[] buf = new byte[len];
			int tlen = 0, offset = 0;
			foreach (BinaryPage p in pages) {
				if (p.Position < pos) {
					tlen = Math.Min((int)(p.Length - (pos - p.Position)), len);
					if (p.Modified) {
						p.Buffer.CopyTo((int)(pos - p.Position), buf, offset, tlen);
					} else {
						stream.Seek(pos - p.Position + p.SourcePosition, SeekOrigin.Begin);
						stream.Read(buf, offset, tlen);
					}
					pos += tlen;
					offset += tlen;

					if (offset >= len)
						break;
				}
			}
			return buf;
		}

		public void MakeBuffer(BinaryPage page)
		{
			if (!page.Modified) {
				byte[] buf = new byte[page.Length];
				stream.Seek(page.SourcePosition, SeekOrigin.Begin);
				stream.Read(buf, 0, page.Length);
				page.Buffer = new List<byte>(buf);
			}
		}

		public void Insert(long pos, byte data)
		{
			IEnumerator<BinaryPage> ie;
			BinaryPage page = GetPageByPos(pos, out ie);
			MakeBuffer(page);
			page.Buffer.Insert((int)(pos - page.Position), data);
			while (ie.MoveNext()) {
				ie.Current.Position++;
			}
		}

		public void InsertRange(byte[] data, long pos, int len)
		{
			IEnumerator<BinaryPage> ie;
			BinaryPage page = GetPageByPos(pos, out ie);
			MakeBuffer(page);
			page.Buffer.InsertRange((int)(pos - page.Position), data);
			while (ie.MoveNext()) {
				ie.Current.Position += data.Length;
			}
		}

		void Delete(long pos)
		{
			IEnumerator<BinaryPage> ie;
			BinaryPage page = GetPageByPos(pos, out ie);
			MakeBuffer(page);
			while (ie.MoveNext()) {
				ie.Current.Position--;
			}
			if (page.Length > 1) {
				page.Buffer.RemoveAt((int)(pos - page.Position));
			} else {
				pages.Remove(page);
			}
		}

		void RemoveRange(long pos, long len)
		{
			int tlen = 0, deleted = 0;
			IEnumerator<BinaryPage> ie = pages.GetEnumerator();
			BinaryPage p;
			while (ie.MoveNext()) {
				p = ie.Current;
				if (p.Position < pos) {
					tlen = (int)Math.Min(p.Length - (pos - p.Position), len);
					if (tlen == p.Length) {
						pages.Remove(p);
					} else {
						MakeBuffer(p);
						p.Buffer.RemoveRange((int)(pos - p.Position), tlen);
					}
					pos += tlen;
					deleted += tlen;

					if (deleted >= len) {
						p.Position = pos;
						break;
					}
				}
			}
			while (ie.MoveNext()) {
				p = ie.Current;
				p.Position -= len;
			}
		}

		void OverWrite(byte data, long pos)
		{
			BinaryPage p = GetPageByPos(pos);
			MakeBuffer(p);
			p.Buffer.RemoveAt((int)(pos - p.Position));
			p.Buffer.Insert((int)(pos - p.Position), data);
		}

		void OverWriteRange(byte[] data, long pos, int len)
		{
			int tlen = 0, offset = 0;
			foreach (BinaryPage p in pages) {
				if (p.Position < pos) {
					tlen = (int)Math.Min(p.Length - (pos - p.Position), len);
					MakeBuffer(p);
					p.Buffer.RemoveRange(offset, tlen);
					byte[] buf = new byte[tlen];
					Array.Copy(data, offset, buf, 0, tlen);
					p.Buffer.InsertRange(offset, buf);
					pos += tlen;
					offset += tlen;

					if (offset >= len)
						break;
				}
			}
		}
	}
	
	class BinaryPage
	{
		public BinaryPage(long pos, int len)
		{
			this.position = pos;
			this.sourcePosition = pos;
			this.length = len;
		}

		long position;

		public long Position
		{
			get { return position; }
			set { position = value; }
		}

		long sourcePosition;

		public long SourcePosition
		{
			get { return sourcePosition; }
		}

		int length;

		public int Length
		{
			get
			{
				if (Modified) {
					return buffer.Count;
				} else {
					return length;
				}
			}
		}

		public bool Modified
		{
			get { return buffer != null; }
		}

		List<byte> buffer;

		public List<byte> Buffer
		{
			get { return buffer; }
			set { buffer = value; }
		}
	}
}

引用返信 編集キー/
■12382 / inTopicNo.13)  Re[12]: 添え字がInt64(long)の可変長リストが欲しい
□投稿者/ れい (359回)-(2008/01/08(Tue) 22:49:00)
No12372 (Hirotow さん) に返信
> <s>こうですか? わかりません><</s>こんな感じですかね?
> うまく動く自信がないorz

ふにゅー
自分で振っておいてなんですが、
ソースを読んで改善点とか間違いとか指摘するほどやる気がありません。
すみません。

でもこれだけコードが組めるHirotowさんならなら、
あとはデバッグとかがんばればできるのではないかと思います。

こういったバッファ・キャッシュ処理は結構めんどくさくて面白くないので、
GiBを超えるファイルを扱えるソフトが少ないのも仕方がありませんね。

一度きちんと動くものを自分なりに作っておけば
次からは比較的楽につくれると思いますので
がんばってください。
引用返信 編集キー/
■12391 / inTopicNo.14)  Re[13]: 添え字がInt64(long)の可変長リスト
□投稿者/ Hirotow (129回)-(2008/01/08(Tue) 23:55:41)
2008/01/09(Wed) 00:26:26 編集(投稿者)
2008/01/09(Wed) 00:26:10 編集(投稿者)

がんばってみます。
でも描画が激遅でへこむorz
TextRendererでセカンダリサーフェスに書くのにトータル0.2秒超えてるし。

追記
現時点での最新版をうpしとくのでどなたかテストしていただけると幸いです。(読み込み→表示→選択(選択時のスクロールなし)まで実装してあります。)
http://mui-style.net/data/BinaryEditor_Mikansei_Rev33.zip
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -