diff --git a/cipher/railfence/railfence.go b/cipher/railfence/railfence.go index aeeaed3fa..f01e73c4f 100644 --- a/cipher/railfence/railfence.go +++ b/cipher/railfence/railfence.go @@ -11,57 +11,53 @@ import ( ) func Encrypt(text string, rails int) string { - if rails == 1 { + runes := []rune(text) + if rails <= 1 || rails >= len(runes) { return text } - // Create a matrix for the rail fence pattern - matrix := make([][]rune, rails) - for i := range matrix { - matrix[i] = make([]rune, len(text)) - } + railsData := make([][]rune, rails) - // Fill the matrix dirDown := false - row, col := 0, 0 - for _, char := range text { + row := 0 + for _, char := range runes { if row == 0 || row == rails-1 { dirDown = !dirDown } - matrix[row][col] = char - col++ + railsData[row] = append(railsData[row], char) if dirDown { row++ } else { row-- } } + var result strings.Builder - for _, line := range matrix { - for _, char := range line { - if char != 0 { - result.WriteRune(char) - } + for _, rail := range railsData { + for _, char := range rail { + result.WriteRune(char) } } return result.String() } + func Decrypt(cipherText string, rails int) string { - if rails == 1 || rails >= len(cipherText) { + cipherRunes := []rune(cipherText) + if rails <= 1 || rails >= len(cipherRunes) { return cipherText } // Placeholder for the decrypted message - decrypted := make([]rune, len(cipherText)) + decrypted := make([]rune, len(cipherRunes)) // Calculate the zigzag pattern and place characters accordingly index := 0 for rail := 0; rail < rails; rail++ { position := rail down := true // Direction flag - for position < len(cipherText) { - decrypted[position] = rune(cipherText[index]) + for position < len(cipherRunes) { + decrypted[position] = cipherRunes[index] index++ // Determine the next position based on the current rail and direction diff --git a/cipher/railfence/railfence_test.go b/cipher/railfence/railfence_test.go index 7f7bfa53a..d6c65d6d7 100644 --- a/cipher/railfence/railfence_test.go +++ b/cipher/railfence/railfence_test.go @@ -2,6 +2,7 @@ package railfence import ( "testing" + "unicode/utf8" ) func TestEncrypt(t *testing.T) { @@ -35,6 +36,30 @@ func TestEncrypt(t *testing.T) { 100, "hi", }, + { + "Encrypt with Unicode runes", + "café crème", + 3, + "c maécèefr", + }, + { + "Encrypt with negative rails", + "hello", + -1, + "hello", + }, + { + "Encrypt with zero rails", + "hello", + 0, + "hello", + }, + { + "Encrypt with empty string", + "", + 3, + "", + }, } for _, test := range railFenceTestData { @@ -78,6 +103,30 @@ func TestDecrypt(t *testing.T) { 100, "hi", }, + { + "Decrypt with Unicode runes", + "c maécèefr", + 3, + "café crème", + }, + { + "Decrypt with negative rails", + "hello", + -1, + "hello", + }, + { + "Decrypt with zero rails", + "hello", + 0, + "hello", + }, + { + "Decrypt with empty string", + "", + 3, + "", + }, } for _, test := range railFenceTestData { @@ -89,3 +138,37 @@ func TestDecrypt(t *testing.T) { }) } } + +func FuzzRailFence(f *testing.F) { + seeds := []struct { + input string + rails int + }{ + {"hello", 2}, + {"hello world", 3}, + {"hello", 1}, + {"hi", 100}, + {"café crème", 3}, + {"the quick brown fox jumps over the lazy dog", 4}, + {"1234567890", 2}, + {"", 3}, + {"a", 2}, + {"hello", -1}, + {"hello", 0}, + } + + for _, seed := range seeds { + f.Add(seed.input, seed.rails) + } + + f.Fuzz(func(t *testing.T, input string, rails int) { + if !utf8.ValidString(input) { + return + } + encrypted := Encrypt(input, rails) + decrypted := Decrypt(encrypted, rails) + if decrypted != input { + t.Fatalf("round-trip failed: original=%q, rails=%d, encrypted=%q, decrypted=%q", input, rails, encrypted, decrypted) + } + }) +}