Solidity: String ve Byte’lar üzerine

Farklı geçmişlerden gelerek Solidity öğrenmeye başlayan geliştiricilerin büyük problemidir Solidity string tipleri. Öncelikle resmi dokümantasyona göz atalım.
String literals are written with either double or single-quotes (
"foo"or'bar'). They do not imply trailing zeroes as in C;"foo"represents three bytes not four. As with integer literals, their type can vary, but they are implicitly convertible tobytes1, …,bytes32, if they fit, tobytesand tostring.String literals support escape characters, such as
\n,\xNNand\uNNNN.\xNNtakes a hex value and inserts the appropriate byte, while\uNNNNtakes a Unicode codepoint and inserts an UTF-8 sequence.
Hmm… bytes! Yani aslında dinamik byte[]. Hemen yine resmi dokümandaki Arrays bölümüne göz atalım
Variables of type
bytesandstringare special arrays. Abytesis similar tobyte[], but it is packed tightly in calldata.stringis equal tobytesbut does not allow length or index access (for now).So
bytesshould always be preferred overbyte[]because it is cheaper.
O halde resmi doküman bizi string işlemleri yapmak için bytes tipine yönlendiriyor. Bytes da bir byte[] türevi olduğundan nasıl yol alabileceğimiz ortaya çıkmış oluyor.
Solidity string tipini aslında sadece konsept olarak kullanıyor. String tipi ile yapılabilecek çok fazla builtin işlem yok aslında, hatta fonksiyondan string dönebilmek bile bir çok Solidity geliştiricisi için yeni bir konsept. Bunun sebebi aslında string operasyonlarının cpu tarafına getirdiği yükler sebebiyle gerçekte pahalı olması. Farklı programlama geçmişinden gelenler String tiplerinin aslında “immutable” yani sabit olduğunu, String üzerinde yapılan değişikliğin aslında bellekte yeni bir string oluşturduğunu bilir. Ayrıca multibyte ya da UTF-8 stringleri oluşturan blokların aslında 1–4 byte aralığında olmasından ötürü aslında stringleri indekslemek ve indekse bağlı işlemler yapmak teknik olarak EVM üzerine ekstra opcode yükü getirerek ekstra gas kullanımına sebep olacak olmasından dolayı Smart Contract geliştiricisi açısından tercih edilmeyen bir durum. Neden böyle olduğunu daha kolay anlamak açısından basit string operasyonlarını simüle eden bir kontrata göz atalım.
Kontratta görüldüğü gibi aslında stringleri karşılaştırmak ve stringleri birbirine eklemekte herhangi bir problem çıkmıyor çünkü stringi dönüştürdüğümüz bytes tipi üzerinde herhangi bir değişiklik yapmıyoruz. Problem ilerleyen fonksiyonlarda ortaya çıkıyor. Örneğin strLen metodunu “Merhaba Dünya!” ile çağırırsanız aşağıdaki gibi bir durumla karşılaşılıyor.

Neden? Çünkü “ü” bloğu bir multibyte daha doğrusu 2 byte. Yukarıda bahsettiğim konu tamamen bu. Diğer metotları incelediğinizde ya da çalıştırdığınızda tamamen aynı sebepten kaynaklanan anomaliler gözlemleyebilirsiniz. Peki ne yapabiliriz? Elbette sadece ASCII seti kullanabilir, string boyutlarını sınırlayabilir ya da çok fazla gas tüketen, UTF-8 tablosundaki byte değerine göre adım sayısı belirleyen lineer parsing işlemi de yapılabilir örneğin;
Fakat bu metodun gas tüketimine bakarsak;

Execution cost’un tek bir çağrıda yaklaşık 10 katına çıktığını görüyoruz. Dolayısıyla bu tip string operasyonları yaparken düşünmek gerekli.
Son olarak bir de compare göz atalım. Solidity arrayleri doğrudan karşılaştırma olanağı tanımıyor. Stringleri karşılaştırırken önce bytes dönüşümü yapıp ardından keccak256() ile hashlerini karşılaştırmak en çok kullanılan ve tavsiye edilen yöntem. Ben bu yöntemi kullanırken önce bytes uzunluğunu kontrol etmeyi tavsiye ediyorum, böylece gas kullanımı biraz daha düşebiliyor. Bir de bu yöntem ile çok düşük olasılıkla iki farklı string için aynı hashler üretilebileceği aklımızda olmalı ama şu an için bu çok dert etmememiz gereken bir durum.