Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

base64 encoding #1

Open
kokoichi206 opened this issue Sep 10, 2022 · 3 comments
Open

base64 encoding #1

kokoichi206 opened this issue Sep 10, 2022 · 3 comments

Comments

@kokoichi206
Copy link
Member

kokoichi206 commented Sep 10, 2022

Logic- RFC4648

The encoding process represents 24-bit groups of input bits as output
strings of 4 encoded characters. Proceeding from left to right, a
24-bit input group is formed by concatenating 3 8-bit input groups.
These 24 bits are then treated as 4 concatenated 6-bit groups, each
of which is translated into a single character in the base 64
alphabet.

6 bit -> 8 bit の1文字に変換するので、33 % は増える(パディングもそれに追加)

仕様

基本は mac に入っている base64 に合わせる

2 つ以上のファイル名が渡されたとき

1 つめのファイルのエンコードの値のみが出力される

@kokoichi206
Copy link
Member Author

kokoichi206 commented Sep 10, 2022

メモ

[]byte to integer

data := binary.BigEndian.Uint32(append(make([]byte, 1), buf...))
// fmt.Println(binary.LittleEndian.Uint32(append(make([]byte, 1), buf...)))
data := int(buf[2]) + int(buf[1])*256 + int(buf[0])*256*256

string に対する index アクセスは別のものが返る

インデックスだとマルチバイト時にどこを指すのかが曖昧なため?

hoge := "hogee"
fmt.Println(string(hoge[0]))
// 104 が出力される

参考

buf := make([]byte, 3) でゼロフィルはされてる?

a だけ書かれたファイルを読み取った時の buf

[97 0 0]

abcd が書かれたファイルを loop で読み取った時の buf 出力

[97 98 99]
[100 98 99]

3 byte にみたない時は前回の値が消えてないので強制的に消してあげる

outer loop を break する

名前をつけてそこを参照しにいく

func ReadAll() {
        ...
	main:
	for {
		v, _ := r.Read(buf)
		switch v {
		case 0:
			break main
		case 1:
			buf[1] = 0
		}
	}
	fmt.Println(buf)
}

参考

go で標準入力を読み込む

func readStandardInput() []byte {
	scanner := bufio.NewScanner(os.Stdin)
	// 1行分スキャン
	scanner.Scan()
	return scanner.Bytes()
}

参考

関数を引数にとる

// ファイルからの読み込みと標準入力からの入力のどちらかで関数を実行する。
func execute(fn func([]byte) string, file []byte, si []byte) string {
	if len(file) != 0 {
		return fn(file)
	}
	return fn(si)
}

@kokoichi206
Copy link
Member Author

kokoichi206 commented Sep 10, 2022

3 byte ごと読む作戦が。。。

EOF が来るまでは確定で 3byte 読み込める想定だったが、どうやらそうではない。。。
例えば jpg だと 1366 * 3byte ごとに、3 byte ちょうどで読み込めてない。

5000 bytes ごとになんかある??

1365
2731
4097
5463
6829
...

解決策

全部読み込んでから、3 byte ずつ使うようにしたらいけた。。。

3 byte ずつの時の方針

	f, err := os.Open("43694.jpg")
	// f, err := os.Open("test.txt")
	if err != nil {
		return
	}
	defer f.Close()

	r := bufio.NewReader(f)
	// 3 bytes ずつ読み込む(24 bits -> 4*6 bits ずつエンコードするため)
	buf := make([]byte, 3)

	var builder strings.Builder

	cnt := 0
READLOOP:
	for {
		v, err := r.Read(buf)
		// if err != nil {
		// 	fmt.Println(err)
		// }
		switch v {
		case 0:
			break READLOOP
		case 1:
			buf[1] = 0
			buf[2] = 0
			// fmt.Println(cnt)
		case 2:
			buf[2] = 0
			// fmt.Println(cnt)
		}

		if e := encode3bytes(buf, v, &builder, err); e != nil {
			fmt.Println("unexpected error occured while encoding")
			return
		}
		cnt += 1
	}
	fmt.Println(builder.String())

@kokoichi206
Copy link
Member Author

kokoichi206 commented Sep 11, 2022

現状の decode 方法だと、元が 3byte に満たない時 NULL が入っちゃう

wiki

ヌル終端文字列(ヌルしゅうたんもじれつ、英語: null-terminated string)とは、文字配列に格納し、ヌル文字('\0'、ASCIIコードではNUL)でその終端(番兵)を表した文字列である

現状の decode

// 渡されたバイト列ををデコードする。
func Decode(buf []byte) string {

	sb := string(buf)
	max := len(sb) / 4

	var res bytes.Buffer
	// 最終行以外をエンコード。
	for i := 0; i < max; i++ {
		b := sb[4*i : 4*i+4]

		data := alphabetMap[string(b[3])] + alphabetMap[string(b[2])]*64 + alphabetMap[string(b[1])]*64*64 + alphabetMap[string(b[0])]*64*64*64

		b1 := data / (256 * 256)
		b2 := (data % (256 * 256)) / 256
		b3 := data % 256
		res.Write([]byte{byte(b1), byte(b2), byte(b3)})
	}

	return res.String()
}

単体テスト時に発見

Screen Shot 2022-09-12 at 1 40 49

diff でも見つかるが、ターミナルでの出力の見た目は同じ。。。

$ diff <(base64 -d encoded_test.txt) <(./base64 -d encoded_test.txt)
Binary files /dev/fd/15 and /dev/fd/16 differ

Screen Shot 2022-09-12 at 1 43 29

NULL は悪者?

jpg にはいっぱい隠れてる。

以下のようにして、test_data/cat.jpg に隠れてる NULL を探すと 1636 個あった!
(前あった問題と同じかも。。。)

func readFile(fileName string) []byte {
	buf, err := ioutil.ReadFile(fileName)
	if err != nil {
		fmt.Printf("Unable to open '%s': No such file or directory", fileName)
		os.Exit(1)
	}
	cnt := 0
	for _, b := range buf {
		if b == 0 {
			cnt += 1
		}
	}
	fmt.Printf("found NULL string %d\n", cnt)
	return buf
}

方針

途中に見つかる NULL 文字は、元のデータ形式によっては十分あり得るので保持。
ただし、最終デコードが NULL(byte の値が 0)であることは、もともとのバイトが 3 なかったことを意味しているので、そこはデコードさせない。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant