2008年12月28日日曜日

DataContractJsonSerializerでDictionaryを表現

DataContractJsonSerializerはまじめなので、.NETとJSONとの双方向で型が復元されることを前提にしています。そのため、Dictionary<TKey,TValue>を素直にobject表現してくれません。(TKeyがstringに限定され、intなどが復元できないため。TValueの型が不明なため。)
具体的にどうなるかというと

new Dictionary<string,object>{ { "abc", 1 }, { "def", "ghi" } };
[{"Key":"abc","Value":1},{"Key":"def","Value":"ghi"}]
になります。
一般的にJSONを扱う人は
{"abc":1,"def":"ghi"}
を期待していることでしょう。

どうにかしたいと思い、必死に対策を考え、遂に実現しました。
class Surrogate: IDataContractSurrogate {
[Serializable]
class JsonDictionary: ISerializable {
Dictionary<string, object> Dictionary { get; set; }
public JsonDictionary( Dictionary<string, object> dictionary ) {
Dictionary = dictionary;
}
public void GetObjectData( SerializationInfo info, StreamingContext context ) {
foreach( var pair in Dictionary )
info.AddValue( pair.Key, pair.Value );
}
}

public Type GetDataContractType( Type type ) {
return type == typeof( Dictionary<string, object> ) ? typeof( JsonDictionary ) : type;
}
public object GetObjectToSerialize( object obj, Type targetType ) {
var dictionary = obj as Dictionary<string, object>;
if( obj == null )
throw new NotImplementedException();
return new JsonDictionary( dictionary );
}

#region NotImplemented
object IDataContractSurrogate.GetCustomDataToExport( Type clrType, Type dataContractType ) {
throw new NotImplementedException();
}
object IDataContractSurrogate.GetCustomDataToExport( MemberInfo memberInfo, Type dataContractType ) {
throw new NotImplementedException();
}
object IDataContractSurrogate.GetDeserializedObject( object obj, Type targetType ) {
throw new NotImplementedException();
}
void IDataContractSurrogate.GetKnownCustomDataTypes( Collection<Type> customDataTypes ) {
throw new NotImplementedException();
}
Type IDataContractSurrogate.GetReferencedTypeOnImport( string typeName, string typeNamespace, object customData ) {
throw new NotImplementedException();
}
CodeTypeDeclaration IDataContractSurrogate.ProcessImportedType( CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit ) {
throw new NotImplementedException();
}
#endregion
}

あとはDataContractJsonSerializerコンストラクタのIDataContractSurrogate引数にSurrogateインスタンスを渡せば、変換してくれます。
もちろん型保証が外れるため、deserializeはできません。

2008年12月16日火曜日

グループ定義の均等化 Balancing group definition

グループ定義の均等化と言われても、何のことかさっぱり想像がつきませんが、.NET Frameworkの正規表現に不思議な機能があります。実際、役立つシーンがあります。
ネストされる()のペアを対応関係を維持しつつマッチさせたい、というときに使えます。具体的にはCの関数呼び出しやキャストなど。

(?:(?'Open'<)[^<>]*)+(?:(?'Close-Open'>)[^<>]*)+
丸括弧だと正規表現エスケープで読みづらくなるため山括弧の一致にしていますが、もちろん丸括弧でも対応できます。

べき乗 pow() xy

ふとべき乗計算を考えてみた。

double pow( double x, int y ){
return y > 0 ? pow( x, y - 1 ) * x : 1;
}
整数の時は簡単。

実数になると
__declspec(noinline) double pow( double x, double y ){
__asm{
fld y
fld x
fyl2x
fst ST(1)
frndint
fxch ST(1)
fsub ST(0), ST(1)
f2xm1
fld1
faddp ST(1), ST(0)
fscale
}
}
なんかinline assemblerを使ってしまった。うん、何やってるのかさっぱりです。命令とそのときのスタック状態を表にすると
命令ST(0)ST(1)ST(2)
fld yy
fld xxy
fyl2xy×log2x
fst ST(1)y×log2xy×log2x
frndint⌊y×log2x⌋y×log2x
fxch ST(1)y×log2x⌊y×log2x⌋
fsub ST(0), ST(1)y×log2x-⌊y×log2x⌋⌊y×log2x⌋
f2xm12y×log2x-⌊y×log2x⌋-1⌊y×log2x⌋
fld112y×log2x-⌊y×log2x⌋-1⌊y×log2x⌋
faddp ST(1), ST(0)2y×log2x-⌊y×log2x⌋⌊y×log2x⌋
fscale2y×log2x-⌊y×log2x⌋×2⌊y×log2x⌋

ここで2y×log2x-⌊y×log2x⌋×2⌊y×log2x⌋ = 2y×log2x-⌊y×log2x⌋+⌊y×log2x⌋ = 2y×log2x = xyです。
はい、さっぱりわかりませんでした。

2008年12月3日水曜日

ファイルの高速コピー

Webサイトのコンテンツを更新するとき、ローカルディスクからWebサーバにアップロード(ファイルのコピー)をしていますが、ファイル数が多くてExplorerじゃとてもとても。
調べたところFastCopyとかコピーツールがありましたが、ふとC#で書いてみたらどうなるんだろう、と。

static void Copy( FileInfo source, FileInfo destination ) {
var src = new FileStream( source.FullName, FileMode.Open, FileAccess.Read, FileShare.None, 4096, true );
var buffer = new byte[ source.Length ];
src.BeginRead( buffer, 0, buffer.Length, rar => {
src.EndRead( rar );
src.Close();
if( destination.Exists )
destination.Attributes &= ~FileAttributes.ReadOnly;
var dst = new FileStream( destination.FullName, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true );
dst.BeginWrite( buffer, 0, buffer.Length, war => {
  dst.EndWrite( war );
dst.Close();
}, null );
}, null );
}
static void Copy( DirectoryInfo source, DirectoryInfo destination ) {
if( !destination.Exists )
destination.Create();
foreach( var src in source.GetDirectories() )
Copy( src, new DirectoryInfo( Path.Combine( destination.FullName, src.Name ) ) );
var srcfiles = source.GetFiles();
foreach( var dst in destination.GetFiles() )
if( !srcfiles.Any( src => String.Equals( src.Name, dst.Name, StringComparison.OrdinalIgnoreCase ) ) )
dst.Delete();
foreach( var src in srcfiles )
Copy( src, new FileInfo( Path.Combine( destination.FullName, src.Name ) ) );
}
static void Main( string[] args ) {
Copy( new DirectoryInfo( args[ 0 ] ), new DirectoryInfo( args[ 1 ] ) );
}

まずはお試しで、属性やタイムスタンプなどは保存しませんが。
で、コピー時間ですがFastCopyが740秒なのに対して、C#は200秒…。
何も考えずに並列でファイルオープンしているためハンドル数やバッファサイズなど問題点はいろいろあるとは思いますが、この所要時間の差はいったい…?!
ちなみにこのコード、途中に1回だけ30秒間ストールが発生しています。この問題を解決すれば更に30秒短くなるはず…。