endian etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
endian etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

15 Nisan 2021 Perşembe

Endianness Nedir?

Endianness Kelimesi Nereden Geliyor?
Açıklaması şöyle. Yani büyük adresten başlayınca Big Endian oluyor.
The terms big-endian and little-endian were introduced by Danny Cohen in 1980 in Internet Engineering Note 137, a memorandum entitled "On Holy Wars and a Plea for Peace", subsequently published in print form in IEEE Computer 14(10).48-57 (1981). He borrowed them from Jonathan Swift, who in Gulliver's Travels (1726) used them to describe the opposing positions of two factions in the nation of Lilliput. The Big-Endians, who broke their boiled eggs at the big end, rebelled against the king, who demanded that his subjects break their eggs at the little end.
Endianness Nedir?
Endianness bellekteki bir adresin CPU'daki registerlara hangi sırada yerleştirildiğidir. Örneğin 0x89ABCDEF değeri big endian mimarisinde okunurken ilk register'a 0x89, ikinci register'a 0xAB değeri yerleştirilir. Ancak register'lar üzerinden düşünmek insanlara zor geldiği için genellikle endiannes sanki bellekle alakalıymış gibi düşünülür. Aslında aynı belleği big endian ya da little endian iki işlemciye de taksak olurdu. Çünkü belleği kontrol eden çip endianness bilmez.

İki endian tipinin farkını 4 byte'lık 42 sayısının bellekte nasıl tutulduğunu görerek anlayabilirizGeleneksel olarak bellek byte[0] -> byte1 -> ... şeklinde gösterilir, burada da gelenek takip ediliyor.
|    | byte[0] | byte[1] | byte[2] | byte[3] |
|----+---------+---------+---------+---------|
| BE |       0 |       0 |       0 |      42 |
| LE |      42 |       0 |       0 |       0 |
Gösterim
Örneğin sistemin endiannes değerini anlamak için elimizde şöyle bir kod olsun
int main()
{
  int x = 1;

  char *y = (char*)&x;

  printf("%c\n",*y+48);
}
Bu kod little endian ise şöyle çalışır
       higher memory
          ----->
    +----+----+----+----+
    |0x01|0x00|0x00|0x00|
    +----+----+----+----+
    A
    |
   &x
Big endian ise şöyle çalışır
    +----+----+----+----+
    |0x00|0x00|0x00|0x01|
    +----+----+----+----+
    A
    |
   &x
Her iki durumda da geleneksel gösterim kullanılıyor ve byte lar soldan sağa olacak şekilde yazılırlar. 

İstisnai olarak byte yerine bit kullanan NATO dokümanları bulunur. Bu dokümanlardaki mesajlar bit-oriented oldukları için bitler sağdan sola olacak şekilde yazılıyorlar.  Bit shift işlemleri endian agnostic olduğu için, endianness belirtmek gerekmiyor.


Büyük Endian (Big Endian)
SPARC gibi işlemciler big endian çalışırlar. Yazmaya sağdan başlanır. Yukarıdaki örnekte 42 en sağdan başlanarak yazılıyor. Dolayısıyla geleneksek gösterim açısından değil ancak Big Endian açısından MSB byte[0], LSB ise byte[3]

Eğer ben bu veriyi göndermek istersem önce 3 sonra 2 sonra 1 ve en son 0 numaralı byte'lar gönderilir.

Küçük Endian (Little Endian)
x86 işlemciler ise küçük endian çalışırlar. MSB sağda, LSB ise solda gibi düşünülür. Yazmaya soldan başlanır. Yukarıdaki örnekte 42 en soldan başlanarak yazılıyor. Dolayısıyla geleneksek gösterim açısından değil ancak Little Endian açısından  MSB byte[3], LSB ise byte[0]

Internette Haberleşme
Internette haberleşmek için Big Endian kullanılır. Dolayısıyla Küçük Endian bir işlemci, veriyi gönderirken byte'ları yer değiştirerek gönderir.

Eğer ben bu veriyi göndermek istersem önce 0 sonra 1 sonra 2 ve en son 3 numaralı byte'lar gönderilir.

Örnek
Şu kod parçası c'ye yine ilk byte'ın değerini verir. Yani c = 1 olur
int a = 1;

char c = (char)a;
Örnek
Ağdan gelen bir veriyi - örneğin 4 byte'lık bir diziyi - küçük endian mimaride int'e cast edersek farklı bir sayı elde ederiz. Bir projede ARP isteği içindeki IP adresini int'e çevirip okurken şöyle bir kod kullandım ve bu başıma geldi.
struct sockaddr_in sai;
memcpy (&sai.sin_addr,buffer.data(), buffer.size());
int result = sai.sin_addr.s_addr;
Bit Oriented Mesajlar
Bit Oriented mesajlarda Endianness önemli değildir. Çünkü veri bitler şeklinde okunur ve yazılır. Bu durumda "Bit Numbering" gösterimi önemli hale geliyor.

1. LSB 0 bit numbering
Açıklaması şöyle
When the bit numbering starts at zero for the least significant bit (LSB) the numbering scheme is called "LSB 0".
Yani bitler şöyle gösterilir
7 - 6 - 5 - 4 - 3 - 2 - 1 - 0
2. MSB 0 bit numbering
Bitler şöyle gösterilir. MSB 0 aslında sadece gösterimsel bir şey. IPv4 örneğinde açıklaması görülebilir.
0 - 1 - 2- 3 - 4 - 5 - 6 - 7 
3. Word Olarak 2 Byte Kullanan Mesajlar
Burada da önce hangi byte'ın gönderildiği (transmission) önemli hale geliyor. 
Örnek - 1553
Bir örnek şöyle
Q: Are 1553 packets Big Endian like Ethernet?
A. No, little endian if we are talking about bits. Bit 15 is sent first (15, 14, 13, 12, ....2, 1, 0). Bit 15 - 8 is the Most significant Byte, while bits 7-0 are the least significant Byte.
Örnek - IPv4
Şeklen şöyle. Burada MSB 0 Numbering kullanılıyor.

Açıklaması şöyle
... for the diagram and discussion, the most significant bits are considered to come first (MSB 0 bit numbering). The most significant bit is numbered 0, so the version field is actually found in the four most significant bits of the first byte,
Açıklaması şöyle
The network byte order does not specify how bits are transmitted over the network. It specifies how values are stored in multi byte fields.

Example:

The Total Length field is composed of two bytes. It specifies in bytes the size of the packet.

Lets say we have the value 500 for that field. Using the Network Byte Order it will be seen over the wire like this, being transmitted transmission from left to right:

00000001 11110100
If we would use the little endian format then it would have been seen over the wire like this:

11110100 00000001
After the whole packet is constructed the bits will be sent starting with the lowest addressed bit of the header (bit 0), so the transmission will start with the Version field.
Örnek - NATO Dokümanları
Bu dokümanlarda bit gösterimi genellikle şöyle
15 - 14 - 13 - 12 - 11 - 10 - 9 - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 - 0




24 Mart 2016 Perşembe

Endian

Endian Nedir?
Endianness Nedir yazısına taşıdım

Sistemin Endian Tipini Anlamak
Sistemin Endian Tipini Anlamak yazısına taşıdım.

Big Endian'a Çevirerek Yazma
C
htonl veya htons ile verilen 32 bit veya 16 bitlik sayılar Big Endian'a çevirilebilir. Örnek:
uint32_t tmp = htonl(any32bitint);
send(socket, (char *) &tmp, sizeof(tmp), 0);
64'bitlik sayıları BigEndian'a çevirmek için aşağıdaki gibi yapılabilir.

int32_t data [2];
data[0] = htonl((int32_t) from);
data[1] = htonl((int32_t) (from >> 32));

int64_t mynum;
from = (int64_t) ntohl(data[1]);
from = (from << 32);
from = from | (int64_t) ntohl(data[0]);
C#
C#'ta BigEndian ile ilgili bir stream sınıfı yok. BinaryWriter sınıfından türeyen ve çok işe yarayan BEBinaryWriter sınıfı burada.

Java
DataOutputStream  
DataOutputStream yazısına taşıdım.

Big Endian'dan Okuma
C
ntohl veya ntohs ile verilen 32 veya 16 bitlik sayılar Big Endian'dan okunabilir. Bu fonksiyonlar unsigned döndürdükleri için, sonucu cast etmek gerekebilir.

ntohl
Şöyle kullanırız:
uint32_t data = ...;
int32_t value = (int32_t) ntohl(data);
Kendi ntohl gerçekleştirimimiz
ntolh'yi kendimiz yazmak isteseydik şöyle yapardık
char b[4] = //...

uint32_t result;
uchar *p = (uchar *)(&result);
*p++ = b[3];
*p++ = b[2];
*p++ = b[1];
*p     = b[0];
Ya da şöyle yapardık:
char* bp = ...;
*(uint32_t*)bp = (uint32_t)(
                (*bp       << 24) |
                (*(bp + 1) << 16) |
                (*(bp + 2) <<  8) |
                (*(bp + 3)      ));
Ya da şöyle yapardık. Bu kod ilk örnek ile aynı aslında.
long NTOHL(long value)
{

    long test = 0x01020304;
    char* ptr = (char*)&test;
    int isSystemAlreadyBigEndian = (ptr[0] == 0x01);

    char* ptrValue = (char*)&value;
    long result = value;
    char* ptrResult = (char*)&result;

    if (!isSystemAlreadyBigEndian)
    {
        // do the byte swap
        ptrResult[0] = ptrValue[3];
        ptrResult[1] = ptrValue[2];
        ptrResult[2] = ptrValue[1];
        ptrResult[3] = ptrValue[0];
    }

    return result;

}


8 byte'lık double okuma da benzer bir mantığa sahip.
char b[8] = //...

double result;
uchar *p = (uchar *)(&result);
*p++ = b[7];
*p++ = b[6];
*p++ = b[5];
*p     = b[4];
*p++ = b[3];
*p++ = b[2];
*p++ = b[1];
*p     = b[0];
C++ ile şöyle yaparız.
int64_t size;
recv(client, (char *)&size, sizeof int64_t, 0);
char *start = (char *)&size, *end = start + sizeof(size);
std::reverse(start, end);
Büyük Endian'dan küçük endian'a çevirmek için template ta kullanabiliriz. Örnek:


Java
ByteBuffer
getDouble
ByteBuffer sınıfının getDouble() vs. metodları ile BigEndian formatı okunabilir. Örnek:

flip
Bu konudan çok emin olmamakla beraber ByteBuffer herhangi bir mevcut buffer'ı wrap etse bile okumaya başlamadan önce fliıp() metodu ile okumaya hazır hale getirmek lazım.

network byte order to host byte order in java sorusunda verilen cevapta ByteBuffer sınıfı okumaya başlarken flip() metodu ile okumaya hazır hale getiriliyor.

flip() metodu aşağıdaki gibi kodlanmış ve amacı ise ByteBuffer sınıfına verilmiş olan limiti en son yazma pozisyonuna eşitlemek çünkü okuma işleminde sınıfın tamponunun sonuna gelip gelinmediği limit değerine bakarak kontrol ediliyor.


Yani netty ile ByteBuffer kullanmak için aşağıdakine benzer bir kod lazım.
ByteBuffer buf = ByteBuffer.allocate(1);
buf.put(0x1c);
buf.flip();//Okumaya hazırla
e.getChannel().write(ChannelBuffers.wrappedBuffer(buf));

DataInputStream
DataOutputStream ve DataInputStream yazısına taşıdım.

QT
QDataStream
QDataStream sınıfıyla ilgili olarak STL,StreamBuffer nesnesi ve QT başlıklı yazıya göz atabilirsiniz. QT ile QIODevice stream sınıflarının temeli gibi düşünülebilir. Qt serialization - how to exclude a field? sorusunda QDataStream sınıfı kullanılarak bir nesneyi binary olarak yazma örneği bulunabilir. Bu iş için aşağıdaki gibi operatörler tanımlamak yeterli.
friend operator << (QDataStream& stream, const MyClass& myclass)

Little Endian'dan Okuma
Java
ByteBuffer
order
Normalde JVM BigEndian çalışır ama Little Endian çalışılmak istenirse order metodu kullanılabilir. Örnek:
  
Integer.reverseBytes
Örnekte küçük endian'dan float okuma ve float yazma gösterilmiş.
Float.intBitsToFloat(Integer.reverseBytes(dataInputStream.readInt()));

C# 
BitConverter
BitConverter sınıfı ile LittleEndian veri okunabilir. 


ToUInt16

Örnek:
byte[] buffer = new byte[2];
ushort value = BitConverter.ToUInt16(buffer, 0);
Küçük Endian Yazma
Java
Örnekte küçük endian yazma gösterilmiş.
dataOutputStream.writeInt(Integer.reverseBytes(Float.floatToRawIntBits(float)));

25 Mart 2014 Salı

C++, Java, QT Stream sınıfları

Stream Sınıfları Hiyerarşisi
Konuyu C++ Streamleri başlıklı yazıya taşıdım.

ios_base ve locale

ios_base sınıfı template parametrelerine bağımlı olmayan ve tüm stream sınıflarının kullandığı ortak özellikleri içerir.

STL ile gelen IOStream kütüphanesinde kullanılan tüm streamlerin içinde bir locale nesnesi bulunur.
Bu durumu gösteren örnek bir şekli buradan aldım.

imbue() metodu kullanılarak stream'in kullandığı locale değiştirilebilir. Ancak When to Imbue a New Locale başlıklı yazıda yazıldığı gibi eğer stream buffer içinde veri varsa locale değiştirmemek gerekir.

Daha detaylı bilgi için Locale İşlemleri başlıklı yazıya göz atabilirsiniz.

C programlarında locale atamakC programlarında setlocale(int category,const char* locale) metodu kullanılır.

Atanmış olan locale'i almak için
setlocale(LC_ALL,NULL) yapılır.
Örnek:
printf ("C locale is : %s\n", setlocale(LC_ALL, 0)) ; //C locale is : C verir
C++ programlarında locale nesnesi yaratmak
facade vererek bir locale yaratmak
Örnek  :
std::locale fct (std::locale::classic(),boost::posix_time::time_input_facet("%d.%m.%Y %H:%M:%S"));
C++ programlarında locale atamak
C++ ile locale yazısına taşıdım.

Java'da ise locale isimleri için Locale.getISOLanguages() ve Locale.getISOCountries() metodları kullanılabilir. Örnek:


Stream'in locale'ini Almak
Örnek:
cout<<"C++ locale: "<<cout.getloc().name();//C++ locale: C

stream ve yardımcı metodlar
peek
Eğer stream'in sonuna geldiyse stream.peek () == EOF döner.

C programları

C programlarında kullanılan locale aşağıdaki şekilde görünen fonksiyonları etkiler.



QT
STL'e alternatif olabilecek bir diğer kütüphaneyi de karşılaştırma yapabilmek için eklemek istedim. QT ile gelen I/O kütüphanesinin hiyerarşisini buradan aldım ve aşağıya ekledim.

QIODevice tüm kütüphanenin başlangıç noktası. STL'den farklı olarak socketler de I/O kapsamında değerlendirilmiş.

Yine STL'den farklı olarak streamlere yazarken encode/decode işlemlerinı yapabilme imkanı sağlayan bir soyutlama katmanı daha getirilmiş ve QDataStream ile QTextStream sınıfları geliştirilmiş. Bu sınıflardan QDataStream big endian veya little endian olarak herhangi bir QIODevice sınıfına yazıp okuyabilme imkanı sağlıyor. Aşağıdaki şekli buradan aldım ve kullanım durumunu gösteriyor.
Reading the same file with several different streams in Qt sorusunda da açıklandığı gibi QDataStream veya  QTextStream sınıfları kullanılsa bile QIODevice sınıfı yazılacak pozisyonu tutuyor.
QTextStream
QTextStream sınıfı ile C'deki sscanf fonsiyonu gibi verilen string'i parçalayarak okuma mümkün. Örnek burada.

QBuffer
QBuffer sınıfı QByteArray sınıfına arayüz gibi kullanılıyor. Böylece herhangi bir şeyi byte array haline çevirmek kolaylaşıyor. Örnek için buradan aldığım koda bakabilirsiniz.

 
Java
Stream ve Karakterler Arasındaki Köprü
Java'da stream ve karakterler arasındaki köprüyü sağlayan iki sınıf var. InputStreamReader ve OutputStreamWriter.

InputStreamReader

Java da geniş bir I/O kütüphanesine sahip. C++ ile aralarındaki en büyük farklardan birisi ise Java ile gelen Reader/Writer sınıfları. istringstream sınıfı bir stream (yani ham byte'lardan) string okuyabilirken aynı işi yapan Java sınıfı ise InputStreamReader. Java InputStreamReader sınıfı sayesinde ham byte'lardan encoderlar vasıtasıyla dönüşüm yapabilme imkanı bulunuyor.

BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream("infilename"), "UTF-8"));
 Soket'tten okuma yapmak içinse
Socket echoSocket = new Socket("myserver", 7);
BufferedReader in = new BufferedReader(new InputStreamReader(
                        echoSocket.getInputStream()));

OutputStreamWriter
Bu işlemin tam tersini yapan sınıf ise OutputStreamWriter.


Sadece Stream Sınıfları

FileInputStream
FileInputStream sınıfını kullanırken en çok dikkat edilmesi gereken konu stream ile işimiz bitince close() metodunun çağırılması. Böylece dosya için konulan kilit bırakılır. Eğer bu metodu çağırmazsak finalize() metodu garbage collector tarafından çağırılıncaya kadar dosya kilitli kalabilir. Force java to release all file locks/handles in Java sorusunda da benzer bir açıklama var.


Sadece Reader Sınıfları

BufferedReader

BufferedReader sınıfı okuma işlemi için tampon belleği kullanarak I/O işlemlerini kolaylaştırır.

readLine metodu

Bu metod ile tüm satırı okuma imkanı var

BufferedReader reader = new BufferedReader(new InputStreamReader(System.in,"UTF-8"));
while (reader.ready()) {
  String line = reader.readLine();
}

Sadece Writer Sınıfları 
PrintWriter
PrintWriter sınıfı verilen OutputStream'i bir Writer haline çeviren köprü işlevini görür. Eğer encoding ile kullanılmak istenirse örnek kod kullanılabilir.


StringWriter
StringWriter sınıfı dosya yerine hafızadaki bir tamponda girdileri biriktirir. println() metodunu sağlayan PrintWriter sınıfı ile beraber kullanılması daha kolaydır. Burada beraber kullanımı gösteriyor.

Scanner

Java zengin olduğu için bir de Scanner sınıfına sahip. Bu sınıf ile Reader/Writer arayüzlerini kullanmadan stream'den okuma yapmak mümkün. Scanner aşağıdaki kurucu metod imzasına sahip.

Örnek
Scanner sc = new Scanner(System.in,"UTF-8");
while(sc.hasNext()) {
  String next = sc.nextLine();
}
Yukarıdaki kullanımda Scanner sınıfı BufferedReader sınıfı gibi kullanılmış. Buradaki soruda ikisinin arasındaki bazı farklardan bahsediliyor. Örneğin Scanner içindeki tampon BufferedReader sınıfından daha küçük ancak esas fark, Scanner sınıfının primitif veri tipleri okumak üzere tasarlanmış olması.

Scanner sınıfının hasNextInt () metodu ise buradaki gibi kullanılabilir.

Bu kullanım şekliyle C'deki scanf'i andırıyor.

scanf'in döngü içinde kullanılabilmesini sebebi ise verilen tipten kaç tane okuyabildiğini dönmesi. Örnek:

Reflection ve Streamler

ObjectOutputStream ve ObjectInputStream sınıfları bir nesneyi stream'e binary olarak yazıp okuyabilmemizi sağlarlar. ObjectOutputStream yazma işleminden önce bir header yazdığı için kaç byte okuyacağını ve her alanın büyüklüğünü bilir. Aşağıdaki örnekte bir nesneyi TCP üzerinden bu sınıfları kullanarak gönderip alma gösterilmiş.