/ 最近 .rdf 追記 編集 設定 本棚

脳log[20170315] ビット演算子多めのヘルパーメソッド群 | ビット演算に伴う整数化。符号付き整数化。符号無し整数化。



2017年03月15日 (水) WSH(JScript)で COBOLアプリケーションのデータファイルを読んで XMLとして書き出して XSLTでソート&HTML化して JavaScriptでフィルタリングして、Firefoxで印刷するなどしていた>20170307, 20170227。フィルタリングは早いうちに特にソートよりも前にしたいのだけど、URLを通して XSLにフィルタリングパラメータを渡す方法がなかったのでしかたなく。パソコンにデータがあるのに手書きでフォームを埋めるとか嫌だよね。フォームごと印刷したれ。■JScriptでやるっていうのは、まあ、.NETがある今は「特にインストールする必要がないっていうのも重要な要素」だとしても意味のない縛りなんだけど、JScript.Netは後付け言語仕様にこれじゃない感があるし、C#はコンパイルが必要だし(他の方法もある?)、PowerShellは実行するまでが面倒くさい>「Windows PowerShell スクリプトを実行する」。それにランタイムは入っていても jsc.exeや csc.exeはないだろう……と思ったけど、例えば C:\Windows\Microsoft.NET\Framework64\v4.0.30319\ にあるのだから、やっぱりどこでも入ってるのかも。今では実行時にコードをあれこれ操作したり作り出したりすることもできるみたいだから、コンパイラのコアもランタイムの一部で、コンパイラはただの UI(ガワ)だったりするだろうか。■データファイルはヘッダ+固定長レコードだった。Shift_JISで読み込むと文字数とバイト数が比例しないし文字コードとバイナリも素直に対応しないので、2バイト単位になるけど Unicodeテキストとして読み込むことでバイナリデータにアクセスできる。こういうときサロゲートペアが2文字扱いになる仕様が重要。ECMAScriptがそうかは知らないけど、よく聞く仕様ではある。■データの型は(桁数を無視すると)3種類だった。Shift_JISの文字列と、Shift_JISの半角数字(※ASCII数字ともいう)と、唯一混ざり込んだバイナリっぽいものが10進1桁を4ビットで表現して最後を 0xC(正符号)でターミネイトした packed decimalだった。これがあるせいで、このバイナリっぽいものがたまたま Shift_JISの先行バイトだったときに、文字数とバイト数の対応や文字コードとバイナリの対応が崩れるせいで、全体を Shift_JISテキストとして読み込んで切り分けることができなかったのだった。■固定長だということや Shift_JISや ASCII文字だということや、16進1桁をそのまま 10進1桁として使用することが、どれもエディタで見てわかることに繋がっていて非常に助かる。可変長だったらヘッダの特定のバイトを読んで長さを確定しないといけないし、テキストデータだからって圧縮されていたら展開しないといけないし、10進3桁(1000通り)を表現するには2進10桁(1024通り)もあれば十分だぜなんて効率を考え出したらバイナリエディタで開いただけで数字が読めたりはしなくなるので。■■■意外に難しい。「4nビットを用いて整数を表現するとき,符号なし固定小数点表示法で表現できる最大値をaとし,BCD(2進化10進符号)で表現できる最大値をbとする。nが大きくなるとa/bはどれに近づくか。」さっきも書いたけど、BCDって言われて10ビットで10進3桁を表現することを考え出すと答えから遠ざかってしまう。■これをダブルクリックしたら入力済みのフォームが表示されるからメニューから印刷したらいいよと一人に説明したら、パソコンに手入力したものを印刷してたのだと思ってたとのこと。そうね、パソコンって使いにくいワープロかもしれないね。

最終更新: 2018-06-09T23:02+0900

[javascript] ビット演算子多めのヘルパーメソッド群

用途はタイトル欄(↑)を参照のこと。JavaScriptのビット演算は事前に整数化が必要なため遅いらしいですよ。

あと、これは JavaScriptではなく JScriptです。関数名でインチキしてるので、って書くまでもなく new ActiveXObject してるのだから明らかだった。

function String.prototype.trim()
{
	return this.replace(/^[\s ]+|[\s ]+$/g, "");
}

function String.prototype.atoi(offset, width) // 引数の単位はバイト。Unicodeにマッピングされた ASCIIではなく、UTF-16の符号単位2バイトを2つの ASCII数字として解釈する。符号は非対応。省略したら文字列の端から端まですべてが対象。
{	// 0x30(0),..., 0x39(9)
	var Offset = offset != null && 0 <= offset && offset < 2*this.length ? (offset>>>0) : 0;
	var Width = width != null && 0 <= width && Offset + width < 2*this.length ? (width>>>0) : 2*this.length - Offset;
	if (Width == 0) {
		return 0; // NaN もありか?
	}
	var x = 0, i = (Offset + 1)>>>1, I = (Offset + Width)>>>1;
	if (Offset & 1) {
		x += (this.charCodeAt(i-1)>>>8) & 0xF;
	}
	if (x == 0) {
		// ありがちな場合のショートカット
		while (i < I && this.charCodeAt(i) == 0x3030) {
			i += 1;
		}
	}
	for (; i < I; ++i) {
		var Ch = this.charCodeAt(i);
		x = x * 10 + (Ch & 0xF);
		x = x * 10 + ((Ch>>>8) & 0xF);
	}
	if ((Offset + Width) & 1) {
		x = x * 10 + (this.charCodeAt(I) & 0xF);
	}
	return x;
}

function String.prototype.asPackedSigned(offset, width) // 引数の単位はいずれもパックされた10進1桁(=4bit)。省略したら文字列の端から端まですべてが対象。
{
	// assert(0 != this.length);
	offset = 0 <= offset ? offset>>>0 : 0;
	width = 1 <= width && width <= 4*this.length-offset ? width>>>0 : 4*this.length-offset;

	var decstr = "";
	for (var i = offset>>>2; i < (offset+width)/4; ++i) {
		var LLHH = this.charCodeAt(i);
		decstr += String.fromCharCode((LLHH>>>4)&0xF|0x30, LLHH&0xF|0x30, (LLHH>>>12)&0xF|0x30, (LLHH>>>8)&0xF|0x30);
	}
	offset &= 3;

	var Sign = {
		"<"/*0x3C*/:+1, "3"/*0x33*/:+1,
		"="/*0x3D*/:-1, "7"/*0x37*/:-1
	}[decstr.charAt(offset+width-1)] || 1;
	return Sign * decstr.substr(offset, width-1); // decstrが 0-9以外の文字(:;<=>?)を含んでると NaN。
}

function String.prototype.endWithSJISLead()
{
	if (! this) {
		return false;
	}
	var Ch = this.charCodeAt(this.length-1) >>> 8;
	return 0x81 <= Ch && Ch <= 0x9F || 0xE0 <= Ch && Ch <= 0xEF;
}

function String.prototype.asSJIS(Mask) // 先頭1バイトを捨てる(Mask=0xFF00), 末尾1バイトを捨てる(Mask=0x00FF), 両方捨てる(Mask=0x0000), そのまま(Mask=0xFFFF,null,undefined)
{
	var Share = {
		old: arguments.callee,
		path: FSO().BuildPath(
			FSO().GetSpecialFolder(2/*TemporaryFolder*/),
			FSO().GetTempName()
		),
		temp: null
	};
	Share.temp = {
		fou: FSO().OpenTextFile(Share.path, 2/*ForWriting*/, true/*creat*/, -1/*Unicode*/),
		fin: FSO().OpenTextFile(Share.path, 1/*ForReading*/, true/*creat*/, 0/*Shift_JIS*/)
	};
	Share.temp.fou.Write(""); // write BOM
	Share.temp.fin.ReadAll(); // drop BOM

	var asSJIS = function(Mask) {
		if (! this) {
			return "";
		}
		if (Mask == null) { // 速度面で意味のあるショートカット。
			Share.temp.fou.Write(this);
			return Share.temp.fin.ReadAll();
		} else {
			var s = this;
			s = String.fromCharCode(s.charCodeAt(0)&(Mask|0xFF00)) + s.slice(1); // s.length == 1 のときを忘れないように。
			s = s.slice(0,-1) + String.fromCharCode(s.charCodeAt(s.length-1)&(Mask|0x00FF)); // s.length == 1 のときを忘れないように。
			Share.temp.fou.Write(s);
			s = Share.temp.fin.ReadAll();
			return s.slice(!(Mask&0x00FF), s.length-!(Mask&0xFF00)); // 後ろから2バイト目が Shift_JISの先行バイトだった場合、捨てるべき1バイトと結びついて1文字になってるだろうから捨てすぎてしまうだろうね。
		}
	};
	asSJIS.clean = function() {
		Share.temp.fou.Close();
		Share.temp.fin.Close();
		FSO().DeleteFile(Share.path);
		String.prototype.asSJIS = Share.old;
	};

	return (String.prototype.asSJIS = asSJIS).call(this, Mask);
	// 循環参照とかありそうでやばくない? 長く実行するわけじゃないけども。
}
function String.prototype.asSJIS.clean()
{
/*
	asSJISを呼び出したのと同じ回数だけ
	ファイルを作成して、削除するのなら、cleanメソッドはいらない。
	一時ファイルが蓄積するのを気にしないのなら、cleanメソッドはいらない。
	どちらも気になるのでテキトーなタイミングで cleanメソッドを呼んで
	一時ファイルを削除する。
	その次の asSJIS呼び出しでまた一時ファイルが作成される。

	この空のメソッドは上記のコメントのために存在しているのではなく、
	一度も asSJISを呼び出さないうちに asSJIS.clean()を呼び出しても
	エラーにならないように置かれている。
*/
}

function Date.prototype.format(Fmt)
{
	var Map = {
		"%": "%"
		,Y : ""+this.getFullYear()
		,m : ("0"+(this.getMonth()+1)).slice(-2)
		,d : ("0"+this.getDate()).slice(-2)
		,H : ("0"+this.getHours()).slice(-2)
		,M : ("0"+this.getMinutes()).slice(-2)
		,S : ("0"+this.getSeconds()).slice(-2)
	};
	return Fmt.replace(/%(.)/g, function($0, $1) {
		return Map[$1] || $0;
	});
}

function Date.prototype.isOlderThan(Other)
{
	return this.valueOf() < Other.valueOf();
}

function FSO()
{
	var FSO_ = new ActiveXObject("Scripting.FileSystemObject");
	return (FSO = function() { return FSO_; })();
}

JScriptの function Declarationはたぶん事前実行される点が違うだけで function Expressionのシンタックスシュガーなんだよね。だからドットでつないだ関数名でプロパティ代入ができる。便利。関数の順番を気にしないで済む。

 @2018-06-09 バグ修正

50行目、String.prototype.asPackedSigned

-	}[decstr.charAt(width-1)] || 1;
+	}[decstr.charAt(offset+width-1)] || 1;

offset が4の倍数でない時に間違った場所を読んでいた。ときどき個数としてありえない負の数が現れて発覚した。2万数千件に対して10数件の割合だったので2か月以上気がつかなかった。無念。

最終更新: 2017-03-17T03:48+0900

[javascript] ビット演算に伴う整数化。符号付き整数化。符号無し整数化。

今まで意識してなかったけど、ビット演算を使って数値を整数化するとき、その結果が符号付き整数か符号無し整数かは重要。ビット演算の前準備としての整数化では32ビット幅への切り詰めが行われるから、すごく大きな正数が切り詰められた結果の32ビット目がたまたま 1 だったときに負数が返ってきたりする。それに驚きたくないなら符号無しのビット演算を使う。

999999999999|0     //=> -727379969
999999999999>>>0   //=> 3567587327
999999999999>>>0|0 //=> -727379969