dynamic array implementation etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
dynamic array implementation etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

23 Kasım 2019 Cumartesi

Offset Kavramı Ve Dinamik Dizi Yapımı (2 ve 3 Boyutlu)

Daha önce Offset Kavramı ve Dinamik Dizi Yapımı isminde bir yazı yazmış ve işaretçiler(Pointer) başka bir deyiş ile adresler ile neler yapabileceğini izah etmeye çalışmıştım.

Dinamik bir diziyi implement etmede izlediğim yol şuydu;

Dinamik bellek yöneten fonksiyonları kullanarak, hafızada bir alan tahsis (allocate) etmek ve sonra, fonksiyonun döndürdüğü adres üzerinden hesaplama yaparak ilgili veriye ulaşmakdı.


Çıkardığım hesaplama formülü ise aşağıdaki gibiydi;
Basit bir hesap ile formülümüz aşağıdaki gibi olacaktır.
Kaç Bayt = index X Veri tipinin kapladığı alan(Byte)
Yeni Adres = Başlangıç adresi + Kaç Byte

Biraz düşünmeyle bulduğum bu formül, sadece 1 boyutlu bir dizinin elemanlarına erişim formülüydü. Şöyle ki;
Element_Adress = Base_Adress  + (Index * Element_Size)
Memory_Size = Element_Count * Element_Size

Sonrasında ise Tuğrul hocamın (Write Great Code: Volume 1: Write Great Code, Write Great Code, Volume 2: Thinking Low-Level, Writing High-Level) kitaplarını kabaca incelerken, "Composite Data Types and Memory Objects" konusu gözüme çarptı ve doğrudan o konuyu incelemeye başladım.Kadere bakın ki; Pointer'lar, diziler ve çok boyutlu dizilerin elemanlarına erişim formülü yer alıyordu. :)

2 Boyutlu Dizi Ve Elemanlarına Erişim Formülü

Element_Adress = Base_Adress + (Col_Index * Row_Size + Row_Index) * Element_Size
Memory_Size = Element_Size * (Col_Count * Row_Count)

3 Boyutlu Dizi ve Elemanlarına Erişim Formülü

Element_Adress = Base_Adress + ( (Depth_Index * Col_Size + Col_Index) * Row_Size + Row_Index) * Element_Size
Memory_Size = Element_Size * Depth_Size * (Col_Count * Row_Count)

Formülleri biraz açıklayacak olursak;
Element_Adress hafıza bloğundaki verinin adresini temsil etmektedir.
Base_Adress dinamik bellekte alan tahsis eden fonksiyonların döndürdüğü adresi temsil etmektedir.
Memory_Size ise dizinin hafızada kapladığı alanı temsil etmektedir.
Element_Size ise sizeof(veri_tipi) ile bulduğumuz değerdir.
Col_Size ise dizideki kolon sayını temsil eder.
Row_Size ise dizideki satır sayısını temsil eder.
Depth_Size ise kolon ve satır ikilisinden kaç adet olacağını temsil eder.

Formülleri bilmek hoştur amma, uygulayabilmek başkadır başka diyerek formülü, yine pointer kullanımının mümkün olduğunu bildiğim dillerde kabaca denemeye çalıştım.
Formüller aynı şekilde, farklı sözdizimlerinde(syntax) kodlanmıştır.

2 Boyutlu Dinamik Array implementasyonu (C++)

/*
  Author   : isocan
  Purpose  : How to implement dimensional dynamic array using pointers.
  DateTime : 17.11.2019

  Write Great Code: Volume 1: Understanding the Machine
  Composite Data Types and Memory Objects
  Type variable[Col_Size][Row_Size]
  Element_Adress = Base_Adress + (Col_Index * Row_Size + Row_Index) * Element_Size
*/
#include <cstdint>
#include <string> 
#include <stdexcept>
#include <sstream>
#include <iostream>

template <class Type>
class DimensionalArray{
private:
   const std::string IndexOutOfRangeException = "IndexOutOfRangeException at "; 
   Type* fBaseAdress;  
   int32_t fColCount;
   int32_t fRowCount;
   int32_t fMemorySize; 
private:
   std::string ToString(int32_t value){
      std::string outString;
      std::stringstream ss;
      ss << value;
      outString = ss.str();
      return outString;
   }

   std::string GetMessage(std::string indexName,int32_t index){
      return std::string(IndexOutOfRangeException + indexName + "["+ ToString(index)+"]");
   }

   void RangeCheck(int32_t colIndex, int32_t rowIndex){
     if ((colIndex < 0) || (colIndex > fColCount - 1))
       throw std::out_of_range(GetMessage("colIndex",colIndex).c_str());
 
     if ((rowIndex < 0) || (rowIndex > fRowCount - 1))
       throw std::out_of_range(GetMessage("rowIndex",rowIndex).c_str());
   }

   int32_t Offset(int32_t colIndex, int32_t rowIndex){
     RangeCheck(colIndex,rowIndex);
     return (colIndex * fRowCount + rowIndex) * sizeof(Type);
   } 

   Type* CalculateElementAdress(int32_t colIndex, int32_t rowIndex){
     return fBaseAdress + Offset(colIndex,rowIndex);
   }

   void CalculateMemorySize(){
     fMemorySize =  sizeof(Type) * (fColCount * fRowCount);
   }
   
   void SetColAndRowCount(int32_t colCount,int32_t rowCount){
      fColCount = colCount;
      fRowCount = rowCount;
   }

   void MemoryAllocate(){
     CalculateMemorySize();
     fBaseAdress = (Type*)malloc(fMemorySize);
   }

   void MemoryFree(){
     free(fBaseAdress);   
   }
public:
  DimensionalArray(int32_t colCount,int32_t rowCount):
    fColCount(colCount),
    fRowCount(rowCount){
    MemoryAllocate();
  }
  ~DimensionalArray() {
    MemoryFree();
  }      
public:
  Type GetElement(int32_t colIndex, int32_t rowIndex){
      Type* elementAdress = CalculateElementAdress(colIndex,rowIndex);
      return *elementAdress;
  }
  
  void SetElement(int32_t colIndex, int32_t rowIndex, Type value){
      Type* elementAdress = CalculateElementAdress(colIndex,rowIndex);
      *elementAdress = value;
  }

  void ReSize(int32_t colCount,int32_t rowCount){
     MemoryFree();
     SetColAndRowCount(colCount,rowCount);
     fMemorySize = CalculateMemorySize();
     fBaseAdress = (Type*)realloc(fBaseAdress,fMemorySize);
  }

  int32_t GetColCount(){
    return fColCount;
  }

  int32_t GetRowCount(){
    return fRowCount;
  }
};


int main(){    
    try{
        DimensionalArray myTwoDimensionArray(2,2); // 2 sütun, 2 satır
        myTwoDimensionArray.SetElement(0,0,31); // 1. kolon, 1. satır.
        myTwoDimensionArray.SetElement(1,0,32); // 2. kolon, 1. satır

        myTwoDimensionArray.SetElement(0,1,33); // 1. kolon, 2. satır
        myTwoDimensionArray.SetElement(1,1,34); // 2. kolon, 2. satır
        
        /*
        int32_t value_0_0 = myTwoDimensionArray.GetElement(0,0); 
        std::cout << "1. kolon, 1. satır Değeri: " << value_0_0 << std::endl;
        int32_t value_1_0 = myTwoDimensionArray.GetElement(1,0); 
        std::cout << "2. kolon, 1. satır Değeri: " << value_1_0 << std::endl;

        int32_t value_0_1 = myTwoDimensionArray.GetElement(0,1); 
        std::cout << "1. kolon, 2. satır Değeri: " << value_0_1 << std::endl;
        int32_t value_1_1 = myTwoDimensionArray.GetElement(1,1);  
        std::cout << "2. kolon, 2. satır Değeri: " << value_1_1 << std::endl;
      */

        // test
        for (size_t colIndex = 0; colIndex < myTwoDimensionArray.GetColCount(); colIndex++){
          for (size_t rowIndex = 0; rowIndex < myTwoDimensionArray.GetRowCount(); rowIndex++){
              int32_t value = myTwoDimensionArray.GetElement(colIndex,rowIndex);
              std::cout << "["<< colIndex << "," << rowIndex <<"]" << value << std::endl;
          }      
        }
    }
    catch(const std::exception& e){
      std::cerr << e.what() << '\n';
    }
    
    system("pause");
    return EXIT_SUCCESS;
}

3 Boyutlu Dinamik Array implementasyonu (C++)

/*
  Author   : isocan
  Purpose  : How to implement Three-dimensional dynamic array using pointers.
  DateTime : 21.11.2019

  Write Great Code: Volume 1: Understanding the Machine
  Composite Data Types and Memory Objects

  Type variable[Depth_Size][Col_Size][Row_Size];
  Element_Adress = Base_Adress + ( (Depth_Index * Col_Size + Col_Index) * Row_Size + Row_Index) * Element_Size
*/
#include <cstdint>
#include <string> 
#include <stdexcept>
#include <sstream>
#include <iostream>

template <class Type>
class ThreeDimensionalArray{
private:
   const std::string IndexOutOfRangeException = "IndexOutOfRangeException at "; 
   Type* fBaseAdress;  
   int32_t fColCount;
   int32_t fRowCount;
   int32_t fDepthSize;
   int32_t fMemorySize;
private:
   std::string ToString(int32_t value){
      std::string outString;
      std::stringstream ss;
      ss << value;
      outString = ss.str();
      return outString;
   }

   std::string GetMessage(std::string indexName,int32_t index){
      return std::string(IndexOutOfRangeException + indexName + "["+ ToString(index)+"]");
   }

   void RangeCheck(int32_t depthIndex, int32_t colIndex, int32_t rowIndex){
    if ((depthIndex < 0) || (colIndex > fDepthSize - 1))
       throw std::out_of_range(GetMessage("depthIndex",depthIndex).c_str());
 
     if ((colIndex < 0) || (colIndex > fColCount - 1))
       throw std::out_of_range(GetMessage("colIndex",colIndex).c_str());
 
     if ((rowIndex < 0) || (rowIndex > fRowCount - 1))
       throw std::out_of_range(GetMessage("rowIndex",rowIndex).c_str());
   }

   int32_t Offset(int32_t depthIndex, int32_t colIndex, int32_t rowIndex){
     RangeCheck(depthIndex,colIndex,rowIndex);    
     return ( (depthIndex * fColCount + colIndex) * fRowCount + rowIndex) * sizeof(Type);
   } 

   Type* CalculateElementAdress(int32_t depthIndex, int32_t colIndex, int32_t rowIndex){
     return fBaseAdress + Offset(depthIndex, colIndex, rowIndex);
   }

   void CalculateMemorySize(){
     fMemorySize = sizeof(Type) * fDepthSize * (fColCount * fRowCount);
   }
   
   void SetDepthAndColAndRowCount(int32_t depthSize,int32_t colCount,int32_t rowCount){
      fDepthSize = depthSize;
      fColCount = colCount;
      fRowCount = rowCount;
   }

   void MemoryAllocate(){
     CalculateMemorySize();
     fBaseAdress = (Type*)malloc(fMemorySize);
   }

   void MemoryFree(){
     free(fBaseAdress);   
   }
public:
  ThreeDimensionalArray(int32_t depthSize,int32_t colCount,int32_t rowCount):
    fDepthSize(depthSize),
    fColCount(colCount),
    fRowCount(rowCount){
    MemoryAllocate();
  }
  ~ThreeDimensionalArray() {
    MemoryFree();
  }      
public:
  Type GetElement(int32_t depthIndex,int32_t colIndex, int32_t rowIndex){
      Type* elementAdress = CalculateElementAdress(depthIndex,colIndex,rowIndex);
      return *elementAdress;
  }
  
  void SetElement(int32_t depthIndex,int32_t colIndex, int32_t rowIndex, Type value){
      Type* elementAdress = CalculateElementAdress(depthIndex,colIndex,rowIndex);
      *elementAdress = value;
  }

  void ReSize(int32_t depthSize, int32_t colCount,int32_t rowCount){
     MemoryFree();
     SetDepthAndColAndRowCount(depthSize,colCount,rowCount);
     CalculateMemorySize();
     fBaseAdress = (Type*)realloc(fBaseAdress,fMemorySize);
  }

  int32_t GetColCount(){
    return fColCount;
  }

  int32_t GetRowCount(){
    return fRowCount;
  }

  int32_t GetDepthSize(){
    return fDepthSize;
  }

  int32_t GetMemorySize(){
    return fMemorySize;
  }
};

int main(){    
    ThreeDimensionalArray anArray(2,2,2); // 2 adet, 2 sütun, 2 satırlık tablo

    // access elements ( set)
    for (size_t depthIndex = 0; depthIndex < anArray.GetDepthSize(); depthIndex++)
         for (size_t colIndex = 0; colIndex < anArray.GetColCount(); colIndex++)
             for (size_t rowIndex = 0; rowIndex < anArray.GetRowCount(); rowIndex++)
                     anArray.SetElement(depthIndex,colIndex,rowIndex, depthIndex + colIndex + rowIndex);
 
    // access elements ( get)
    for (size_t depthIndex = 0; depthIndex < anArray.GetDepthSize(); depthIndex++)
         for (size_t colIndex = 0; colIndex < anArray.GetColCount(); colIndex++)
             for (size_t rowIndex = 0; rowIndex < anArray.GetRowCount(); rowIndex++)
                   std::cout <<  anArray.GetElement(depthIndex,colIndex,rowIndex) << std::endl;
  
    system("pause");
    return EXIT_SUCCESS;
}
2 Boyutlu Dinamik Array implementasyonu (delphi)

{
  Author   : isocan
  Purpose  : How to implement dimensional dynamic array using pointers.
  DateTime : 17.11.2019

  Write Great Code: Volume 1: Understanding the Machine
  Composite Data Types and Memory Objects
  Type variable[Col_Size][Row_Size]
  Element_Adress = Base_Adress + (Col_Index * Row_Size + Row_Index) * Element_Size
}

unit DimensionalArray;

interface

uses
  Winapi.Windows,
  System.Classes,
  System.SysUtils;


type

  TDimensionalArray = class
   const IndexOutOfRangeException = 'IndexOutOfRangeException at %d';
  type
    PT = ^T;
  strict private
    FColCount: NativeInt;
    FRowCount: NativeInt;
    FMemorySize : NativeInt;
    FBaseAdress: PT;
    FPtrWrapper : TPtrWrapper;
  strict private
    procedure RangeCheck(AColIndex, ARowIndex: NativeInt);
    function Offset(AColIndex, ARowIndex: NativeInt): NativeInt;
    function GetElement(AColIndex, ARowIndex: NativeInt): T;
    procedure SetElement(AColIndex, ARowIndex: NativeInt; const Value: T);
    function CalculateElementAdress(AColIndex, ARowIndex: NativeInt): PT;
    procedure CalculateMemorySize();
    procedure SetColAndRowCount(AColCount, ARowCount: NativeInt);
    procedure MemoryAllocate();
    procedure MemoryFree();
  public
    constructor Create(AColCount, ARowCount: NativeInt);
    destructor Destroy();
  public
    procedure ReSize(AColCount, ARowCount: NativeInt);
  public
    property ColCount: NativeInt read FColCount;
    property RowCount: NativeInt read FRowCount;
    property Element[AColIndex, ARowIndex: NativeInt]: T read GetElement write SetElement; default;
  end;

implementation

{ TDimensionalArray }
constructor TDimensionalArray.Create(AColCount, ARowCount: NativeInt);
begin
  SetColAndRowCount(AColCount,ARowCount);
  MemoryAllocate();
  inherited Create;
end;

destructor TDimensionalArray.Destroy;
begin
  TMarshal.FreeMem(FPtrWrapper);
  inherited Destroy;
end;


procedure TDimensionalArray.RangeCheck(AColIndex, ARowIndex: NativeInt);
begin
  if (AColIndex < 0) or (AColIndex > FColCount-1 ) then
     raise Exception.Create(Format(IndexOutOfRangeException,[AColIndex]));

  if (ARowIndex < 0) or (ARowIndex > FRowCount-1 ) then
     raise Exception.Create(Format(IndexOutOfRangeException,[ARowIndex]));
end;

function TDimensionalArray.Offset(AColIndex, ARowIndex: NativeInt)
  : NativeInt;
begin
  RangeCheck(AColIndex,ARowIndex);
  Result := (AColIndex * FRowCount + ARowIndex) * SizeOf(T);
end;

procedure TDimensionalArray.ReSize(AColCount, ARowCount: NativeInt);
begin
  MemoryFree();
  SetColAndRowCount(AColCount,ARowCount);
  CalculateMemorySize();
  FPtrWrapper := TMarshal.ReallocMem(FPtrWrapper,FMemorySize);
  FBaseAdress := FPtrWrapper.ToPointer;
end;

procedure TDimensionalArray.SetColAndRowCount(AColCount, ARowCount: NativeInt);
begin
  FColCount := AColCount;
  FRowCount := ARowCount;
end;

procedure TDimensionalArray.MemoryAllocate();
begin
  CalculateMemorySize();
  FPtrWrapper := TMarshal.AllocMem(FMemorySize);
  FBaseAdress := FPtrWrapper.ToPointer;
end;

procedure TDimensionalArray.MemoryFree;
begin
  TMarshal.FreeMem(FPtrWrapper);
end;

function TDimensionalArray.CalculateElementAdress(AColIndex,
  ARowIndex: NativeInt): PT;
begin
  Result := PT(PByte(FBaseAdress) + Offset(AColIndex, ARowIndex));
end;

procedure TDimensionalArray.CalculateMemorySize();
begin
  FMemorySize := SizeOf(T) * (FColCount * FRowCount);
end;

function TDimensionalArray.GetElement(AColIndex, ARowIndex: NativeInt): T;
begin
  Result := CalculateElementAdress(AColIndex, ARowIndex)^;
end;

procedure TDimensionalArray.SetElement(AColIndex, ARowIndex: NativeInt;
  const Value: T);
begin
  CalculateElementAdress(AColIndex, ARowIndex)^ := Value;
end;

end.
3 Boyutlu Dinamik Array implementasyonu (delphi)

{
  Author   : isocan
  Purpose  : How to implement Three-dimensional dynamic array using pointers.
  DateTime : 21.11.2019

  Write Great Code: Volume 1: Understanding the Machine
  Composite Data Types and Memory Objects

  Type variable[Depth_Size][Col_Size][Row_Size];
  Element_Adress = Base_Adress + ( (Depth_Index * Col_Size + Col_Index) * Row_Size + Row_Index) * Element_Size
}

unit ThreeDimensionalArray;

interface

uses
  Winapi.Windows,
  System.Classes,
  System.SysUtils;

type

  TThreeDimensionalArray = class
   const IndexOutOfRangeException = 'IndexOutOfRangeException at %s %d';
  type
    PT = ^T;
  strict private
    FDepthSize :NativeInt;
    FColCount: NativeInt;
    FRowCount: NativeInt;
    FMemorySize : NativeInt;
    FBaseAdress: PT;
    FPtrWrapper : TPtrWrapper;
  strict private
    function GetMessage(AIndexName:string;AIndex:NativeInt):string;
    procedure RangeCheck(ADepthIndex,AColIndex, ARowIndex: NativeInt);
    function Offset(ADepthIndex,AColIndex, ARowIndex: NativeInt): NativeInt;
    function GetElement(ADepthIndex,AColIndex, ARowIndex: NativeInt): T;
    procedure SetElement(ADepthIndex,AColIndex, ARowIndex: NativeInt; const Value: T);
    function CalculateElementAdress(ADepthIndex,AColIndex, ARowIndex: NativeInt): PT;
    procedure CalculateMemorySize();
    procedure SetDepthAndColAndRowCount(ADepthSize,AColCount, ARowCount: NativeInt);
    procedure MemoryAllocate();
    procedure MemoryFree();
  public
    constructor Create(ADepthSize,AColCount, ARowCount: NativeInt);
    destructor Destroy();
  public
    procedure ReSize(ADepthSize,AColCount, ARowCount: NativeInt);
  public
    property DepthSize: NativeInt read FDepthSize;
    property ColCount: NativeInt read FColCount;
    property RowCount: NativeInt read FRowCount;
    property MemorySize: NativeInt read FMemorySize;
    property Element[ADepthIndex,AColIndex, ARowIndex: NativeInt]: T read GetElement write SetElement; default;
  end;

implementation

{ TDimensionalArray }
constructor TThreeDimensionalArray.Create(ADepthSize,AColCount, ARowCount: NativeInt);
begin
  SetDepthAndColAndRowCount(ADepthSize,AColCount,ARowCount);
  MemoryAllocate();
  inherited Create;
end;

destructor TThreeDimensionalArray.Destroy;
begin
  TMarshal.FreeMem(FPtrWrapper);
  inherited Destroy;
end;

function TThreeDimensionalArray.GetMessage(AIndexName: string;
  AIndex: NativeInt): string;
begin
   Result := Format(IndexOutOfRangeException,[AIndexName,AIndex]);
end;

procedure TThreeDimensionalArray.RangeCheck(ADepthIndex, AColIndex,
  ARowIndex: NativeInt);
begin
  if (ADepthIndex < 0) or (ADepthIndex > Pred(FDepthSize) ) then
     raise Exception.Create(GetMessage('ADepthIndex',ADepthIndex));

  if (AColIndex < 0) or (AColIndex > Pred(FColCount) ) then
     raise Exception.Create(GetMessage('AColIndex',AColIndex));

  if (ARowIndex < 0) or (ARowIndex > Pred(FRowCount) ) then
     raise Exception.Create(GetMessage('ARowIndex',ARowIndex));
end;

function TThreeDimensionalArray.Offset(ADepthIndex,AColIndex, ARowIndex: NativeInt)
  : NativeInt;
begin
  RangeCheck(ADepthIndex,AColIndex, ARowIndex);
  Result := ( (ADepthIndex * FColCount + AColIndex) * FRowCount + ARowIndex) * SizeOf(T);
end;

procedure TThreeDimensionalArray.ReSize(ADepthSize,AColCount, ARowCount: NativeInt);
begin
  MemoryFree();
  SetDepthAndColAndRowCount(ADepthSize,AColCount,ARowCount);
  CalculateMemorySize();
  FPtrWrapper := TMarshal.ReallocMem(FPtrWrapper,FMemorySize);
  FBaseAdress := FPtrWrapper.ToPointer;
end;

procedure TThreeDimensionalArray.SetDepthAndColAndRowCount(ADepthSize,AColCount, ARowCount: NativeInt);
begin
  FDepthSize := ADepthSize;
  FColCount := AColCount;
  FRowCount := ARowCount;
end;

procedure TThreeDimensionalArray.MemoryAllocate();
begin
  CalculateMemorySize();
  FPtrWrapper := TMarshal.AllocMem(FMemorySize);
  FBaseAdress := FPtrWrapper.ToPointer;
end;

procedure TThreeDimensionalArray.MemoryFree;
begin
  TMarshal.FreeMem(FPtrWrapper);
end;

function TThreeDimensionalArray.CalculateElementAdress(ADepthIndex,AColIndex,
  ARowIndex: NativeInt): PT;
begin
  Result := PT(PByte(FBaseAdress) + Offset(ADepthIndex,AColIndex, ARowIndex));
end;

procedure TThreeDimensionalArray.CalculateMemorySize();
begin
  FMemorySize := SizeOf(T) * FDepthSize * (FColCount * FRowCount);
end;

function TThreeDimensionalArray.GetElement(ADepthIndex,AColIndex, ARowIndex: NativeInt): T;
begin
  Result := CalculateElementAdress(ADepthIndex,AColIndex, ARowIndex)^;
end;

procedure TThreeDimensionalArray.SetElement(ADepthIndex,AColIndex, ARowIndex: NativeInt;
  const Value: T);
begin
  CalculateElementAdress(ADepthIndex,AColIndex, ARowIndex)^ := Value;
end;

end.
2 Boyutlu Dinamik Array implementasyonu (C#)

/*
  Author   : isocan
  Purpose  : How to implement dimensional dynamic array using pointers.
  DateTime : 18.11.2019

  Write Great Code: Volume 1: Understanding the Machine
  Composite Data Types and Memory Objects
  Type variable[Col_Size][Row_Size]
  Element_Adress = Base_Adress + (Col_Index * Row_Size + Row_Index) * Element_Size

  https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-7.3/blittable (Unmanaged type constraint)
*/

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

unsafe class DimensionalArray where T : unmanaged
{
    private int colCount;
    private int rowCount;
    private int memorySize;
    private IntPtr baseAdress;

    public DimensionalArray(int colCount, int rowCount)
    {
        SetColAndRowCount(colCount, rowCount);
        MemoryAllocate();
    }

    private int Offset(int colIndex, int rowIndex)
    {
        return (colIndex * rowCount + rowIndex) * Marshal.SizeOf(typeof(T));
    }

    private IntPtr CalculateElementAdress(int colIndex, int rowIndex)
    {
        IntPtr elementAdress = IntPtr.Add(baseAdress, Offset(colIndex, rowIndex));
        return elementAdress;
    }

    private void CalculateMemorySize()
    {
        memorySize = Marshal.SizeOf(typeof(T)) * (colCount * rowCount);
    }

    private void SetColAndRowCount(int colCount, int rowCount)
    {
        this.colCount = colCount;
        this.rowCount = rowCount;
    }

    private void MemoryAllocate()
    {
        CalculateMemorySize();
        baseAdress = Marshal.AllocHGlobal(memorySize);
    }

    private void ReSize(int colCount, int rowCount)
    {
        /*
         cb:IntPtr
         The new size of the allocated block. 
         This is not a pointer; it is the byte count you are requesting, cast to type IntPtr. 
         If you pass a pointer, it is treated as a size.
         https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.reallochglobal?view=netframework-4.8
         */
        MemoryFree();
        SetColAndRowCount(colCount, rowCount);
        CalculateMemorySize();
        baseAdress = Marshal.ReAllocHGlobal(baseAdress, (IntPtr)memorySize);
    }

    private void MemoryFree()
    {
        Marshal.FreeHGlobal(baseAdress);
    }

    public int ColCount { get { return colCount; } }
    public int RowCount { get { return rowCount; } }

    private T* GetElementAdress(int colIndex, int rowIndex)
    {
        void* unTypedElementAdress = CalculateElementAdress(colIndex, rowIndex).ToPointer();
        T* typedElementAdress = (T*)unTypedElementAdress;
        return typedElementAdress;
    }

    public T this[int colIndex, int rowIndex]
    {
        get
        {
            return *GetElementAdress(colIndex, rowIndex);
        }
        set
        {
            *GetElementAdress(colIndex, rowIndex) = value;
        }
    }

    ~DimensionalArray()
    {
        MemoryFree();
    }
}

class Program
{
    static void Main(string[] args)
    {
        DimensionalArray myTwoDimensionArray = new DimensionalArray(2, 2);
        myTwoDimensionArray[0, 0] = 31;
        myTwoDimensionArray[1, 0] = 32;
        myTwoDimensionArray[0, 1] = 33;
        myTwoDimensionArray[1, 1] = 34;

        // test
        for (int colIndex = 0; colIndex < myTwoDimensionArray.ColCount; colIndex++)
        {
            for (int rowIndex = 0; rowIndex < myTwoDimensionArray.RowCount; rowIndex++)
            {
                int value = myTwoDimensionArray[colIndex, rowIndex];
                MessageBox.Show(value.ToString());
            }
        }
    }
}
3 Boyutlu Dinamik Array implementasyonu (C#)

/*
  Author   : isocan
  Purpose  : How to implement Three-dimensional dynamic array using pointers.
  DateTime : 21.11.2019

  Write Great Code: Volume 1: Understanding the Machine
  Composite Data Types and Memory Objects

  Type variable[Depth_Size][Col_Size][Row_Size];
  Element_Adress = Base_Adress + ( (Depth_Index * Col_Size + Col_Index) * Row_Size + Row_Index) * Element_Size

  https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-7.3/blittable (Unmanaged type constraint)
*/

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

unsafe class ThreeDimensionalArray where T : unmanaged
{
    private const string IndexOutOfRangeException = "IndexOutOfRangeException at %s %d";

    private int depthSize;
    private int colCount;
    private int rowCount;
    private int memorySize;
    private IntPtr baseAdress;

    public ThreeDimensionalArray(int depthSize, int colCount, int rowCount)
    {
        SetDepthAndColAndRowCount(depthSize, colCount, rowCount);
        MemoryAllocate();
    }

    private string GetMessage(string indexName, int index)
    {
        return string.Format(IndexOutOfRangeException, indexName, index);
    }

    private void RangeCheck(int depthIndex, int colIndex, int rowIndex)
    {
        if ((depthIndex < 0) || (colIndex > depthSize - 1))
            throw new Exception(GetMessage("depthIndex", depthIndex));

        if ((colIndex < 0) || (colIndex > colCount - 1))
            throw new Exception(GetMessage("colIndex", colIndex));

        if ((rowIndex < 0) || (rowIndex > rowCount - 1))
            throw new Exception(GetMessage("rowIndex", rowIndex));
    }

    private int Offset(int depthIndex, int colIndex, int rowIndex)
    {
        RangeCheck(depthIndex, colIndex, rowIndex);
        return ((depthIndex * colCount + colIndex) * rowCount + rowIndex) * Marshal.SizeOf(typeof(T));
    }

    private IntPtr CalculateElementAdress(int depthIndex, int colIndex, int rowIndex)
    {
        IntPtr elementAdress = IntPtr.Add(baseAdress, Offset(depthIndex, colIndex, rowIndex));
        return elementAdress;
    }

    private void CalculateMemorySize()
    {
        memorySize = Marshal.SizeOf(typeof(T)) * this.depthSize * (this.colCount * this.rowCount);
    }

    private void SetDepthAndColAndRowCount(int depthSize, int colCount, int rowCount)
    {
        this.depthSize = depthSize;
        this.colCount = colCount;
        this.rowCount = rowCount;
    }

    private void MemoryAllocate()
    {
        CalculateMemorySize();
        baseAdress = Marshal.AllocHGlobal(memorySize);
    }

    private void ReSize(int depthSize, int colCount, int rowCount)
    {
        /*
         cb:IntPtr
         The new size of the allocated block. 
         This is not a pointer; it is the byte count you are requesting, cast to type IntPtr. 
         If you pass a pointer, it is treated as a size.
         https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.reallochglobal?view=netframework-4.8
         */
        MemoryFree();
        SetDepthAndColAndRowCount(depthSize, colCount, rowCount);
        CalculateMemorySize();
        baseAdress = Marshal.ReAllocHGlobal(baseAdress, (IntPtr)memorySize);
    }

    private void MemoryFree()
    {
        Marshal.FreeHGlobal(baseAdress);
    }

    public int DepthSize { get { return depthSize; } }
    public int ColCount { get { return colCount; } }
    public int RowCount { get { return rowCount; } }
    public int MemorySize { get { return memorySize; } }

    private T* GetElementAdress(int depthIndex, int colIndex, int rowIndex)
    {
        void* unTypedElementAdress = CalculateElementAdress(depthIndex, colIndex, rowIndex).ToPointer();
        T* typedElementAdress = (T*)unTypedElementAdress;
        return typedElementAdress;
    }

    public T this[int depthIndex, int colIndex, int rowIndex]
    {
        get
        {
            return *GetElementAdress(depthIndex, colIndex, rowIndex);
        }
        set
        {
            *GetElementAdress(depthIndex, colIndex, rowIndex) = value;
        }
    }

    ~ThreeDimensionalArray()
    {
        MemoryFree();
    }
}

class Program
{
    static void Main(string[] args)
    {
        ThreeDimensionalArray anArray = new ThreeDimensionalArray(2, 2, 2);
        //anArray.MemorySize;

        // set values
        for (int depthIndex = 0; depthIndex < anArray.DepthSize; depthIndex++)
            for (int colIndex = 0; colIndex < anArray.ColCount; colIndex++)
                for (int rowIndex = 0; rowIndex < anArray.RowCount; rowIndex++)
                    anArray[depthIndex, colIndex, rowIndex] = depthIndex + colIndex + rowIndex;

        // access elements
        for (int depthIndex = 0; depthIndex < anArray.DepthSize; depthIndex++)
        {
            for (int colIndex = 0; colIndex < anArray.ColCount; colIndex++)
            {
                for (int rowIndex = 0; rowIndex < anArray.RowCount; rowIndex++)
                {
                    int value = anArray[depthIndex, colIndex, rowIndex];
                }
            }
        }
    }
}
Güncel proje dosyalarına erişim :
Dynamic Array Implementation[Dimensional]
Dynamic Array Implementation[Three-Dimensional]

8 Eylül 2018 Cumartesi

Offset Kavramı ve Dinamik Dizi Yapımı

Birçok programlama dilinde dinamik dizi kullanabiliyoruz.Dizilerde [] indexer operator yardımıyla dizi elemanlarına erişebiliyoruz.
C++

   int* myArray = new int[2];
   myArray[0] = 10;
   delete[] myArray;
C#

   int[] myArray = new int[2];
   myArray[0] = 10;
Java

   int[] myArray = new int[2];
   myArray[0] = 10;
Delphi

var
  MyArray: array of Integer;
begin
  SetLength(MyArray, 2);
  MyArray[0] := 10;
end.
Yukarıdaki örnekleri incelediğimizde C++, C#, Java gibi dillerde new operatörü, delphi için ise SetLength fonksiyonu yardımıyla dinamik dizileri kullanabiliyoruz.

C dilinde ise; C++, C#, java da bulunan new operatoru ya da delphi'deki gibi bir SetLength fonksiyonu bulunmuyor.Dolayısı ile dinamik dizimizi kendimiz gerçekleştirmemiz gerekiyor.
Aşağıdaki örnekte; 8 byte'lık bir hafıza bloğu tahsis edilip, myArray işaretçisinin [] indexer operatörü yardımıyla değerlere erişiyoruz.

 int* myArray = malloc(sizeof(int) * 2);
 myArray[0] = 10;
 myArray[1] = 20;
 free(myArray);
Sanırım nihayet konuya giriş yapabilmek için gerekli alt yapıyı sağlamış oldum :)

Peki dil C, C++, C#, java ya da delphi ya da bir başka dil farketmeksizin myArray[index] operatorü ile dizi elemanlarına erişmek istediğimde, hafıza bloğunda ilgili index'deki veriye nasıl eriştiğini biliyor muyum ? Bilmiyorum.



Bu yazıda dil seviyesinde index mantığını nasıl gerçekleştirebiliriz bunu anlamaya ve öğrenmeye çalışacağız. Bilindiği üzere C, C++, C#, Delphi gibi dillerde, işaretçileri(pointer), işaretçi aritmetiğini(pointer math) kullanabiliyoruz.Java'da ise yine bildiğim kadarıyla şuan için işaretçi kullanımına dil seviyesinde izin verilmiyor.

İşaretçi(pointer) kullanımına izin veren dillerde index'leme mantığını işaretçiler üzerinden verinin adresini hesaplayarak gerçekleştireceğiz.

Dinamik bellek yöneten fonksiyonlar veriyi hafızada tahsis ettikten sonra geriye, veri bloğunun başlangıç adresini untyped pointer(void* veya PVOID veya LPVOID veya Pointer veya IntPtr) tipinde döndürmektedir. C

void *        _RTLENTRY _EXPFUNC malloc(_SIZE_T __size);
Windows API

WINBASEAPI
_Ret_maybenull_
_Post_writable_byte_size_(dwBytes)
DECLSPEC_ALLOCATOR
LPVOID
WINAPI
HeapAlloc(
    _In_ HANDLE hHeap,
    _In_ DWORD dwFlags,
    _In_ SIZE_T dwBytes
    );
C#

IntPtr p = Marshal.AllocHGlobal(size);
Delphi

procedure GetMem(var P: Pointer; Size: Integer);

function HeapAlloc(hHeap: THandle; dwFlags, dwBytes: DWORD): Pointer; stdcall;
UnTyped Pointer'larda aritmetik işlemlere izin verilmediği için, fonksiyonlardan dönen veri tipini typed pointer'lara çevireceğiz. Örneğin C dilinde aşağıdaki gibi untyped pointer tipinde ki p değişkeni için p += 4; işlemine izin verilmeyecektir.

void* p = malloc(sizeof(int) * 2);
p += 4;
[bcc32 Error] main.c(17): E2453 Size of the type 'void' is unknown or zero
Error C2036 'void *': unknown size

Örneğin Delphi dili için aşağıdaki gibi untyped pointer tipide ki P := P + 4; ya da Inc(P,4); işlemine izin verilmeyecektir.

var
  P: Pointer;
begin
  GetMem(P, SizeOf(Integer) * 2);
  P := P + 4;
end.
[dcc32 Error] Project2.dpr(60): E2015 Operator not applicable to this operand type
ya da

var
  P: Pointer;
begin
  GetMem(P, SizeOf(Integer) * 2);
  Inc(P, 4);
end.
[dcc32 Error] Project2.dpr(70): E2001 Ordinal type required
Dolayısı ile hafıza adreslerini index yoluyla hesaplayabilmemiz için typed pointerları kullanmamız gerekiyor.
C

 int* p = malloc(sizeof(int) * 2);
 *p = 10;
  p += 4;
 *p = 20;
free(p);
Delphi

var
  P: ^Integer; // ya da PInteger
begin
  GetMem(P, SizeOf(Integer) * 2);
  P^ := 10;
  Inc(P, 4);
  P^ := 20;
  FreeMem(P, SizeOf(Integer) * 2);
end;
C#

public static IntPtr Add(IntPtr pointer, int offset);
C#'da ise IntPtr(Pointer ya da Handle veri tipi için) yapısında bulunan Add methodu yardımıyla hesaplama yapabiliyoruz.

static void Main(string[] args)
{
  IntPtr p = Marshal.AllocHGlobal(sizeof(int) * 2);
  Marshal.WriteInt32(p, 10);
  IntPtr newP = IntPtr.Add(p, 4);
  Marshal.WriteInt32(newP, 20);
  Marshal.FreeHGlobal(p);
}
Yukarıdaki gibi hafıza bloğunun başlangıç adresini gösteren bir işaretçi üzerinde "doğrudan" değişiklik yapan p += 4; ya da P := P + 4; gibi aritmetik işlemler yazacağımız index'leme mantığı için çok doğru olmayabilir.O nedenle dinamik bellekte veriye işaret eden işaretçi üzerinde doğrudan bir değişiklik yapmadan yeni adresi hesaplayabiliriz.

Basit bir hesap ile formülümüz aşağıdaki gibi olacaktır.
Kaç Bayt = index X Veri tipinin kapladığı alan(Byte)
Yeni Adres = Başlangıç adresi + Kaç Byte

Kabaca başlangıç adresine kaç byte ekleyeceğimizi index yoluyla hesaplayarak verinin offset'ini hesaplamış olduk. Formülü kodlayacak olursak;
C

int index = 0;
int* p = malloc(sizeof(int) * 2);
int* newp = p + (index * sizeof(int));
*newp = 10;
free(p);
Delphi

var
  P, NewP: PInteger;
  Index: Integer;
begin
  Index := 0;
  GetMem(P, SizeOf(Integer) * 2);
  NewP := PInteger(PByte(P) + (Index * SizeOf(Integer)));
  NewP^ := 10;
  FreeMem(P, SizeOf(Integer) * 2);
end;
index değişkeninin değerini değiştirerek dizinin elemanlarına erişim sağlayabiliriz. Yazdığımız kodları eleştirecek olursak; Kodumuz çok da yakışıklı olmadı.
Örneğin; index * sizeof(int) yerine bir offset fonksiyonu yazabiliriz. C

int Offset(int index){
  return index * sizeof(int);
}
int* newp = p + Offset(index);
Delphi

function Offset(Index: Integer): Integer;
begin
  Result := Index * SizeOf(Integer);
end;
NewP := PInteger(PByte(P) + Offset(Index));
Dizinin eleman sayısından, hafızada tahsis edilecek veri bloğununun kaplayacağı alanı hesaplayan bir fonksiyon yazılabilir.
C

int* p = malloc(sizeof(int) * 2);
yerine

int MemSize(int count) {
   return sizeof(int) * count;
}
int* p = malloc(MemSize(2));
Delphi

GetMem(P, SizeOf(Integer) * 2); 
yerine

function MemSize(Count: Integer): Integer;
begin
  Result := SizeOf(Integer) * Count;
end;
GetMem(P, MemSize(2));
Eleştirmeye devam edecek olursak;
Şuana kadar yazdığımız kodların tamamı int veri tipi üzerine idi. Programlama sadece int veri tipinden ibaret değil ki ?
Başka veri tipleri için ne yapacağız ?
Aynı mantığı her veri tipi için tekrar tekrar mı yazacağız ?

İşte tam bu noktada, algoritmayı veri tipinden soyutlamak ya da bağımsız kılmak için, nesne yönelimli programlamanın sağladığı faydalardan yararlanabiliriz. Bunun için C++'da templates, C# ve Delphi'de generics'ler ile daha esnek kodlamalar yapabiliriz.Generics ve templates ile her ne kadar veri tipini esnek hale getirmiş olsak da, algoritmamız her veri tipi için uygun olmayabilir.Bu nokta da generics constraint'ler ile belli tipler için kurallar getirebiliriz. Tabi ki yine C'de untyped pointerlar ile daha generic fonksiyonlar mümkün.

Bahsettiğim generic haline Dynamic Array Implementation adresinden erişebilir, katkı sağlayabilir (mümkünse), gördüğünüz eksik ya da yanlışlar konusunda bilgilendirirseniz sevinirim.

Sağlıcakla.