Jump to page: 1 2
Thread overview
Literal HexaDecimal Kullanırken (devam)
October 31

Merhaba,

Önceki başlıktan devam...

D'de kolaylık olsun diye gerek binary (0b000_1111: bu kalkabilir!), gerekse hexadecima(0x0000_FFFF: bunu konuşcaz) hazır değerler (literal) kullanabiliyoruz. Hatta daha ileri gidip bellek tahsis etmeden, (belki katma ve şablonlarınızda kullanmak üzere) string karşılığını üretmek mümkün:

  import core.internal.string;
  char[20] buf;

  auto neBu = 123456789.unsignedToTempString(buf[]);
  assert(neBu == "123456789");
  assert(is(typeof(neBu) == char[]));
  assert(neBu.length == 9);

  auto aynısı = 0x075bcd15.unsignedToTempString(buf[]);
  assert(neBu == aynısı);
  assert(0x075bcd15 == 123456789);

Sayı tabanını unsignedToTempString!16 şeklinde değiştirip farklı sonuçlar almak da mümkün. Başka örnekler için buraya bakabilrsiniz:

https://github.com/dlang/dmd/blob/master/druntime/src/core/internal/string.d

Konumuz dışı ama D hakkında soru işaret kalmasın. Octal sayılar ise karışıklık olabileceği için std.conv.octal'daki şablonu şu şekilde üzerine bastırarak kullanılıyor:

  import std.conv : octal;
  assert(octal!100 == 0x40);

Dip Not: Başlıktaki -literal için "hazır bilgi" gibi bir Türkçe çeviri yapabiliriz ya da kısaca kelimesi kelimesine diyebiliriz.

October 31
On 10/31/22 00:47, Salih Dincer wrote:

> literal için "hazır bilgi" gibi bir Türkçe
> çeviri yapabiliriz ya da kısaca kelimesi kelimesine diyebiliriz.

Ben "hazır değer" demişim:

  http://ddili.org/sozluk.html

Ali

November 01

On Monday, 31 October 2022 at 07:47:47 UTC, Salih Dincer wrote:

>

Sayı tabanını unsignedToTempString!16 şeklinde değiştirip farklı sonuçlar almak da mümkün. Başka örnekler için buraya bakabilrsiniz:

https://github.com/dlang/dmd/blob/master/druntime/src/core/internal/string.d

Şimdi dikkat lütfen, burda çok ders var! Çok farklı sonuçlar alabileceğiniz olasılıklar var. Yani şu an biz basit bir dönüştürme işlemi yapmıyoruz. Bellek tahsis ve temsil yetenekleri açısında üzeriden geçilmesi gereken konular var. Basitten başlayıp sırayla gidelim:

Önce modüller arası alanda şöyle bir başlangıç kodumuz olsun:

module main;

auto hexToString(T)(T n) {
  import core.internal.string;
  char[32] buff;
  char[] result = n.unsignedToTempString!16(buff[]);
  return result;
}

enum veri = 0x07_5b_cd_15;
auto test = veri.hexToString;

İşlenen hazır değer (literal: enum veri) D derleyicisinin anlayabileyeceği ve özel bir şeye ihtiyaç bırakmadan size_t sayısına çevirebileceği bir şey. Yani önek (prefix) ve ayraçlar (alt çizgilerin sayılar daha fazla da olabilir) görmezden gelinir. Bu basit bir şey...

Ancak dikkat, aşağıda (1.bölümde) kanıtlandığı gibi bunun çiftlerden oluşan hexadecimal bir metin verisi olmadığına dikkat edin. Çünkü sayı 0 ile başlıyor ve chunks(2) gibi bir yöntem kullanıp da .length % 2 == 0 kontrolü yapmazsanız çuvallarsınız!

void main()
{
/************************* 1.BÖLÜM: */
  assert(test.length == 7);
  string metin = "0" ~
                 cast(string)test;
  import std.stdio;
  metin.writeln;

/************************* 2.BÖLÜM: */
  union Convert {
    uint data;
    ubyte[4] cell;
  }
  Convert convert;

  import std.algorithm,
         std.conv,
         std.range;

  metin.chunks(2)
       .map!(bin => bin
       .to!ubyte(16))
       .copy(convert.cell[].retro);

  assert(convert.data == veri);
}

Bunu görmek için isterseniz, ilgili bölümdeki satırı string metin = //"0" ~ şeklinde kısmen yorum satırına dönüştürün.

Diğer bölüm ise bu konu kadar basit! Çünkü aynı bellek bölgesini paylaşan (union ile overlapping yapılan) veri parçalara bölünerek kopyalanıyor ve ekstra işlem yapılmadan tekrar başladığımız noktaya (123456789 sayısına) dönülüyor.

November 01

On Tuesday, 1 November 2022 at 05:01:06 UTC, Salih Dincer wrote:

>

Şimdi dikkat lütfen, burda çok ders var! Çok farklı sonuçlar alabileceğiniz olasılıklar var.

Örneğin, bir önbellek (buffer) belirtmezseniz modülün 76. satırındaki işleve uğrar ve alias get this'in istendiği gibi çalışmadığını görürsünüz:

import std.stdio;
import core.internal.string;

auto foo(ubyte n = 171)
{
  return n.unsignedToTempString!16;
}

void main()
{
  typeof(foo()).stringof.writeln(": ",
         ubyte.max.foo().get);
  assert(ubyte.max.foo[0] == 'f');
  assert(ubyte.max.foo[1] == 'f');
}

-ref. https://github.com/dlang/dmd/blob/master/druntime/src/core/internal/string.d#L76

Bu da bizi private olan bir yapının get()'ni kullanma zorunluluğuna sokuyor! (*bu önemli duruma değincez...)

Ya da bir başkası istediğimiz olan zaten 31. satırda durmaktayken, ubyte için sadece 2 elemanlı bir önbellek kullanabilirsiniz. Ama dikkat, derleme hatası almamak için dönüş değerlerine bir bakın, o bile güven vermiyor!

void main()
{
  auto bar(ubyte n = 171)
  {
    char[2] yerel;
    auto result = n.unsignedToTempString!16(yerel);
    return result;
  }

  //UnsignedStringBuf/*
  char[2]//*/
  harici;
  auto zar(ubyte n = 171)
  {
    auto result = n.unsignedToTempString!16(harici);
    return result;
  }
  writefln("%s", bar());/*
  bar().writeln;//*/
  zar().writeln(" (zar)");
  assert(cast(string)zar() == "ab");
}

-ref. https://github.com/dlang/dmd/blob/master/druntime/src/core/internal/string.d#L31

Çiftli örneğimiz (bar/zar) gerçekten ilginç. Mesela bar() yerel bir önbelleğe sahip olduğundan mı; -bilmiyorum, nedir?- %s kullanmadığınızda konsol çıktısı alamıyorsunuz!

(*) Yıldızlı konumuza dönersek; sanırım private olma ve/veya @property kullanılmama nedeniyle böyle bir sorun yaşamaktayız. Çünkü başka modülde olduğumuz için, modüle özel bir get() özelliğine nasıl erişebiliyoruz ki?

Belki de en iyisi 31. satırı alıp doğrudan kendi yapınıza koymak olabilir. Alternatifler de var...

(DEVAMI GELECEK)

November 01
On 10/31/22 22:01, Salih Dincer wrote:

> auto hexToString(T)(T n) {
>    import core.internal.string;
>    char[32] buff;
>    char[] result = n.unsignedToTempString!16(buff[]);
>    return result;
> }

O işlevde bir hata var: Döndürülen 'result' stack'i göstermektedir. (Mesajlarının geri kalanını hızlıca gözden geçirdim; bu hatadan bahsettiğini farketmedim.)

Hata, programa "stack'i bozuyoruz" yazdıran satır gibi bir şey eklediğimizde kendini gösteriyor:

import std;

auto hexToString(T)(T n) {
  import core.internal.string;
  char[32] buff;
  // writeln("buff   : ", buff.ptr);
  char[] result = n.unsignedToTempString!16(buff[]);
  // writeln("içeride: ", result.ptr);
  return result;
}

void main() {
    enum veri = 0x07_5b_cd_15;
    auto test = veri.hexToString;
    // writeln("dışarıda: ", test.ptr);
    writeln("stack'i bozuyoruz... ", iota(10).sum);
    writeln(test);
}

Diğer writeln'ları test'in nasıl stack'i gösterdiğini belirtmek için kullandım.

dmd bu hatayı ancak şu koşullarda gösteriyor:

- Kod @safe olmalı

- Program -dip1000 komut satırı seçeneği ile derlenmeli

O zaman şöyle diyor:

deneme.d(3325): Error: scope variable `result` may not be returned

Ali

November 01

On Tuesday, 1 November 2022 at 14:44:58 UTC, Ali Çehreli wrote:

>

On 10/31/22 22:01, Salih Dincer wrote:

>

auto hexToString(T)(T n) {
import core.internal.string;
char[32] buff;
char[] result = n.unsignedToTempString!16(buff[]);
return result;
}

O işlevde bir hata var: Döndürülen 'result' stack'i göstermektedir. (Mesajlarının geri kalanını hızlıca gözden geçirdim; bu hatadan bahsettiğini farketmedim.)

Evet hocam, denemeleri run.dlang.io üzerinde yaparken, bazen "Server error: " hatası alıyor ve anlam veremiyordum. Orada herhangi bir bilgilendirme yapılmadığı gibi derleyici de yakalamadı. En son iota'nın olduğu satırı eklemeyince şu ikili denemede hiçbir hata almadım:

import std.stdio, std.range;
import core.internal.string;

enum n = 123456789;

char[32] harici;
char[] foo;

static this()
{
  foo = n.unsignedToTempString(harici[]);
}

void main()
{
  writeln("harici.ptr  : ", harici.ptr);
  auto bar()(size_t n)
  {
    char[32] yerel;
    writeln("yerel.ptr   : ", yerel.ptr);
    char[] result = n.unsignedToTempString!16(yerel[]);
    return result;
  }
  auto b = bar(n);

  //writeln("stack'i bozuyoruz... ", iota(3));
  foo.writeln;
  b.writeln;
} /* ÇIKTISI:
harici.ptr  : 7F7A0E3C8070
yerel.ptr   : 7FFC4EC69950
123456789
75bcd15
*/

Yorum satırını açınca eski sürüm bir derleyici ile derlendiğini görürken son satırdaki 75bcd15 çıktısı kayboluyor. Öte taraftan bar()'a erişim sağlamayınca hiçbir sorun olmuyor. Aradaki fark nedir?

Teşekkürler...

November 01
On 11/1/22 10:04, Salih Dincer wrote:

> derleyici de yakalamadı.

Şimdilik -dip1000 diyerek etkinleştirdiğimiz olanak ileride dile eklendiğinde derleyici yakalayacak.

> En son `iota`'nın olduğu satırı eklemeyince şu ikili
> denemede hiçbir hata almadım:

Hatanın nereden kaynaklandığın anlıyor musun? Buradaki hata, tanımsız davranış (undefined behavior) çeşitten olduğundan programın nasıl davrandığı konusunda bir şey söyleyemeyiz. Dolayısıyla "hiçbir hata almadım" da diyemeyiz. :) Çünkü tanımsız davranış, programın doğru çalıştığı izlenimine de yol açabilir.

>    auto bar()(size_t n)
>    {
>      char[32] yerel;
>      writeln("yerel.ptr   : ", yerel.ptr);
>      char[] result = n.unsignedToTempString!16(yerel[]);

unsignedToTempString'in buradaki kullanımındaki gibi bellek alan işlevler, sonucu o belleğe yazarlar. Bunun amacı, bellek ayırma işlemini en aza indirmektir. Yoksa, unsigned bir türün dizgi gösterimini nereye yazsın? Tabii ki yeni bellek ayıracaktır.

Not: Aslında unsignedToTempString'in kendi içindeki static bir diziye yazma gibi çözümler de denenmiştir ama bu tür işlevleri yarardan çok zarar getirirler. En azından "nonreentrant"tırlar. Yani, bir sonraki çağrıda a belleğin üzerine yazılacaktır.

Burada bir programcı hatası var: unsignedToTempString'e 'yerel'e yaz diyoruz ama sonra yerelin bir bölümünü döndürüyoruz. 'yerel'in stack'te yaşamaktadır ve bar'ın çağrılmasından bir sonraki herhangi işlev çağrısında 'yerel'in üzerine yazılacaktır. (Stack (çağrı yığıtı) öyle işler.)

>    //writeln("stack'i bozuyoruz... ", iota(3));

İşte, o işlem 'yerel'in eski yerini kullanıyor.

>    foo.writeln;

O işlem ise 'yerel'in eski yerini kullanmıyormuş. Bir açıdan şanslı olduğumuz düşünülebilir ama aslında çok şanssız olduğumuzu gösterir: Bu hata gizli kalmış oluyor. Belki aylar sonra yapılan bir değişiklikte ortaya çıkacak, belki son kullanıcıların elinde patlayacak, vs.

> Yorum satırını açınca eski sürüm bir derleyici ile derlendiğini görürken

Tanımsız davranış...

> son satırdaki 75bcd15 çıktısı kayboluyor.

Aslında kaybolmuyor ama şansına görünmeyen karakterler yazdırılmaya çalışılıyor. Benim Emacs penceremdeki çıktı:

harici.ptr  : 7FC7D731DB70
yerel.ptr   : 7FFCDB5712A0
stack'i bozuyoruz... [0, 1, 2]
123456789
^RW\333\374^?^@^@     <-- BURADA

> Öte taraftan `bar()`'a erişim
> sağlamayınca hiçbir sorun olmuyor. Aradaki fark nedir?

Anlatmaya çalıştım. Bu hatayı en ufak bir eksiklik bırakmadan anlamak zorundasın! :)

Ali

November 02

On Tuesday, 1 November 2022 at 17:30:25 UTC, Ali Çehreli wrote:

>

Anlatmaya çalıştım. Bu hatayı en ufak bir eksiklik bırakmadan anlamak zorundasın! :)

Anlamış mıyım hocam :)

import std.array : appender;
import core.internal.string;
import std.stdio, std.range;

enum n = 123456789; // 0x75bcd15

// safe method:
  auto foo(T)(T n)
  if(__traits(isUnsigned, T))
  {
    char[T.sizeof << 1] buffer;
    auto result = appender!(char[]);

    result.put(n.unsignedToTempString!16(buffer));
    return cast(string)result.data;
  }

// poor method:
  auto bar(T)(T n)
  {
    char[T.sizeof << 1] buffer;
    auto result = n.unsignedToTempString!16(buffer);

    return cast(string)result;
  }

void main()
{
  ubyte ab = 171;

  foo(ab).writeln;/*
  n.bar.writeln;//*/

  iota(4).retro.writeln(" hoopp stack'i bozuyoruz :)");
}
November 01
On 11/1/22 21:21, Salih Dincer wrote:

> // safe method:
>    auto foo(T)(T n)
>    if(__traits(isUnsigned, T))
>    {
>      char[T.sizeof << 1] buffer;

Metni oraya yazdıracaksın.

>      auto result = appender!(char[]);

Sonra oraya kopyalayacaksın.

>      result.put(n.unsignedToTempString!16(buffer));

Ama oradaki .put işlemi appender'ın bellek ayırmasına neden olur.

Bellek ayırmak günah değil ama zaten ayıracaktıysak neden unsignedToTempString()'in 'buffer' alanını kullandık.

Başından beri aklımda olan soru burada tekrar ortaya çıkıyor: unsignedToTempString'i nereden öğrendin? Belgelerde vs. yok ama kendi bilgisayarımda grep marifetiyle şurada buluyorum:

  /usr/include/dlang/dmd/core/internal/string.d

Orada unsignedToTempString'in buffer almayan yüklemesi de var. Onu da kullanabiliriz:

import core.internal.string;
import std.stdio;

// simple method:
auto foo(T)(T n)
if(__traits(isUnsigned, T))
{
    return n.unsignedToTempString!16();
}

void main()
{
  ubyte ab = 171;

  // Uyarı: foo()'nun döndürdüğü yapı nesnesi aşağıdaki
  // satırda sonlanır. O yüzden, get'in döndürdüğü değeri
  // sonradan kullanmak üzere saklayamayız. (Biz de öyle
  // yapmıyoruz zaten.)
  foo(ab).get.writeln;
}

O işlevin döndürdüğü TempStringNoAlloc yapı şablonuna baktığımızda içinde char[20] veya char[65] türünde bir üyesi olduğunu görüyoruz. O yüzden bellek ayırmıyor.

> // poor method:

"poor" fazla hoşgörülü bir seçim olmuş çünkü o işlev açıkça hatalıdır. :) ("poor" o anlamı taşımaz.)

>    iota(4).retro.writeln(" hoopp stack'i bozuyoruz :)");

Ama ondan sonra da işlem yapmıyoruz. O yüzden stack'in bozulup bozulmaması bir şeyi değiştirmiyor.

Ali

November 02

On Wednesday, 2 November 2022 at 06:14:04 UTC, Ali Çehreli wrote:

>

On 11/1/22 21:21, Salih Dincer wrote:
Başından beri aklımda olan soru burada tekrar ortaya çıkıyor: unsignedToTempString'i nereden öğrendin? Belgelerde vs. yok ama kendi bilgisayarımda grep marifetiyle şurada buluyorum:

/usr/include/dlang/dmd/core/internal/string.d

Tabii ki büyük usta Steven'dan :)

https://forum.dlang.org/post/rhufca$16n7$1@digitalmars.com

Şöyle bir şeye ne dersin hocam? Bir taşla 2 kuşu, string girer ve 6 char'lık buffer'ı kullanıp string çıkartır:

//*  sharing method of buffer:
auto hex(string  s) @safe
{
  import core.internal.string;
  import std.format, std.range;
	
  char[6] buffer;
  string result;
  foreach(c; s.chunks(2))
  {
    auto str = buffer[4..$];
         str.put(c);
    uint n = buffer[4];
         n |= buffer[5] << 4;
    result ~= n.unsignedToTempString!16(buffer).format!"0%s";	
  }
  return result;
}//*/

unittest
{
  // D Lang => 244, 65c 67e
  assert("D Lang".hex == "0244065c067e");
}

Bu kodun önbelleğinin (char[6] buffer) ilk 4 baytını çevirme, son 2 baytını birleştirme için kullandım. Neticede @safe içinde ve hiçbir bellek taşması ve benzeri yok öyle değil mi? Yani kendi kümesinde öten bir horoz misali kimseyi rahatsız etmiyor olmalı :)

Sevgiler, saygılar...

« First   ‹ Prev
1 2