Arief Sujatmiko on Blogspot
blog ini tempat saya share tentang pemikiran dan kejadian-kejadian yang saya temui, update terbaru follow my twitter @ariefsujatmiko.
Jumat, 21 Agustus 2026
Performa AMD Ryzen 5 3400G iGPU dengan DDR4 3200 MHz untuk Llama.cpp
Kamis, 23 April 2026
Building New PC To Study LLM
Since early 2025, I've been using AI LLMs like ChatGPT or DeepSeek to learn programming or help with research. Using AI LLMs is easier because they can collect and process data into information that's easier for me to understand.
At the time, I didn't know that AI LLMs could be run on a local computer. After reading several articles about SLMs (Small Language Models) that can be run on a local computer, I became interested in running AI LLMs on my computer.
For the software, I tried using Llama.cpp, which has acceleration support for Vulkan.
Initially, I used the computer I usually use for gaming in my spare time. The specifications are as follows:
- Motherboard: Intel H81
- Processor: Intel Core i5 4670 3.4-3.7 GHz Quad Core.
- RAM: 16 GB DDR3 1600 MHz.
- GPU: Nvidia GeForce GTX 1050 4 GB DDR5.
- OS: Windows 10
- SSD: 256 GB.
- Hard Disk: 1 TB.
With the above specifications, I successfully ran the LLM model with 4B and 7B parameters, but it would crash if the parameters were set higher. For 4B parameters, with CPU-only, it produced 5 tokens/s, while with Vulkan acceleration, it could reach 11 tokens/s. Meanwhile, for LLM, with 7B parameters, with CPU-only, it produced 3 tokens/s, while with Vulkan acceleration, it could reach 8 tokens/s.
From the measurement results above, I concluded that the bottleneck was the DDR3 RAM speed and the GPU's RAM size, which was too small. Therefore, LLM with parameters of 7B or higher would use more RAM.
To address the above issues, I finally allocated the budget to upgrade my computer to:
- Motherboard: MSI A520.
- Processor: AMD Ryzen 5 3400G 3.6-4.2 GHz Quad Core with hyperthreading.
- RAM: 64 GB DDR4 3200MHz.
- GPU: AMD Vega 11 integrated 2 GB shared RAM.
- OS: Windows 10.
- SSD: 256 GB.
- Hard Disk: 1 TB.
The iGPU is able to use all available RAM and not limited by shared RAM setting in the BIOS. On Windows computers, when 50% of RAM is used, it will start aggressively swapping RAM, making the computer very sluggish. On Linux, this parameter can be changed, for example, starting when there's 10% RAM remaining. Since I have a relatively large amount of RAM, I intentionally disabled the swap file so I could use more than 50% of RAM for running LLM. The maximum stable RAM speed is at 3200Mhz even though the RAM specification is 3600 MHz, possibly due to a defect in the RAM due to overclocking by previous owner or limitations of the Ryzen 3400G's memory controller. Luckily it can run very stable at 3200Mhz without any problems.
With these new specifications, I was able to run LLM models with 35B and even 80B parameters with LLama.cpp in Vulkan mode. On this computer, Qwen 3.0 Coder Next 80B A3B could run at 10 tokens/s with iGPU. LLama.cpp with Vulkan mode is more power efficient and quiter than the CPU only mode.
Beside running LLM this computer is good for casual gaming too and only consuming 24 watt at idle. The AMD Radeon GPU driver for Windows have great compatibility with games. The Age of Empires HD Edition is broken when viewing civilization's tech tree with my GTX 1050 but run flawlessly with the Ryzen 3400G iGPU.
Kamis, 22 Januari 2026
Searching Algorithms That I Often Use
1. Hash Map
- type
- IListIterator = interface
- ['{16585733-6438-4D58-A772-FC6811EB19BB}']
- procedure First;
- function Next: TObject;
- procedure Delete;
- end;
- { THashable }
- THashable = class(TObject)
- private
- FNext__: THashable;
- protected
- FHash: Integer;
- public
- constructor Create;
- function IsEqual(AKey: Pointer): Boolean; virtual;
- property Next__: THashable read FNext__;
- property Hash: Integer read FHash;
- end;
- { THashMap }
- THashMap = class(TObject)
- private
- FBucket: PObjectArray;
- FCapacity: integer;
- FCount: Integer;
- procedure SetCapacity(NewCapacity: Integer);
- public
- function ObjectByKey(AKey: Pointer): THashable;
- protected
- procedure Grow; virtual;
- function HashKey(AKey: Pointer): Integer; virtual;
- property Capacity: integer read FCapacity write SetCapacity;
- property Count: Integer read FCount;
- public
- destructor Destroy; override;
- procedure Clear;
- function GetObject(AKey: Pointer): THashable;
- procedure Put(AItem: THashable);
- procedure Remove(const AKey: Pointer);
- function GetIterator: IListIterator;
- end;
- { THashMapIterator }
- THashMapIterator = class(TInterfacedObject, IListIterator)
- private
- FHashMap: THashMap;
- FIndex: Integer;
- FNode, FBefore: THashable;
- FReread: Boolean;
- public
- constructor Create(AHashMap: THashMap);
- procedure First;
- function Next: TObject;
- procedure Delete;
- end;
- { THashable }
- constructor THashable.Create;
- begin
- FHash := 0;
- FNext__ := nil;
- end;
- function THashable.IsEqual(AKey: Pointer): Boolean;
- begin
- Result := False;
- end;
- { THashMap }
- procedure THashMap.Clear;
- var
- I: integer;
- begin
- if (FBucket <> nil) then
- begin
- for I := 0 to FCapacity - 1 do
- begin
- if (FBucket^[I] <> nil) then FreeAndNil(FBucket^[I]);
- end;
- FreeMem(FBucket);
- FBucket := nil;
- end;
- FCapacity := 0;
- FCount := 0;
- end;
- destructor THashMap.Destroy;
- begin
- Clear;
- inherited;
- end;
- function THashMap.GetObject(AKey: Pointer): THashable;
- var
- P: THashable;
- H: integer;
- begin
- if (FCount > 0) then
- begin
- H := HashKey(AKey);
- P := THashable(FBucket^[H mod FCapacity]);
- while ((P <> nil) and ((P.Hash <> H) or not P.IsEqual(AKey))) do
- P := P.FNext__;
- Result := P;
- end
- else
- Result := nil;
- end;
- procedure THashMap.Grow;
- var
- Delta: Integer;
- begin
- if FCapacity > 64 then
- Delta := FCapacity div 4
- else if FCapacity > 8 then
- Delta := 16
- else
- Delta := 4;
- SetCapacity(FCapacity + Delta);
- end;
- function THashMap.HashKey(AKey: Pointer): Integer;
- begin
- Result := Integer(AKey);
- end;
- function THashMap.ObjectByKey(AKey: Pointer): THashable;
- begin
- Result := GetObject(AKey);
- if (Result = nil) then
- raise EListError.CreateFmt(SListItemNotFoundError,[IntToHex(Integer(AKey),8)]);
- end;
- procedure THashMap.Put(AItem: THashable);
- var
- I: integer;
- begin
- if (AItem = nil) then exit;
- Inc(FCount);
- if ((FCount * 4) div 3 > FCapacity) then Grow;
- I := AItem.Hash mod FCapacity;
- AItem.FNext__ := THashable(FBucket^[I]);
- FBucket^[I] := AItem;
- end;
- procedure THashMap.Remove(const AKey: Pointer);
- var
- P, Q: THashable;
- I, H: Integer;
- begin
- if (FCount > 0) then
- begin
- H := HashKey(AKey);
- I := H mod FCapacity;
- P := THashable(FBucket^[I]);
- Q := nil;
- while ((P <> nil) and (P.Hash <> H) and not P.IsEqual(AKey)) do
- begin
- Q := P;
- P := P.FNext__;
- end;
- if (P <> nil) then
- begin
- if (Q = nil) then
- FBucket^[I] := P.FNext__
- else
- Q.FNext__ := P.FNext__;
- P.Free;
- Dec(FCount);
- end;
- end;
- end;
- function THashMap.GetIterator: IListIterator;
- begin
- Result := THashMapIterator.Create(Self);
- end;
- procedure THashMap.SetCapacity(NewCapacity: Integer);
- var
- P, Q: THashable;
- NewList: PObjectArray;
- I, J: Integer;
- begin
- if ((NewCapacity = FCapacity) or (NewCapacity < FCount)) then exit;
- if (NewCapacity > 0) then
- begin
- GetMem(NewList,NewCapacity*SizeOf(TObject));
- FillChar(NewList^,NewCapacity*SizeOf(TObject),0);
- for I := 0 to FCapacity - 1 do
- begin
- P := THashable(FBucket^[I]);
- while (P <> nil) do
- begin
- Q := P;
- P := P.FNext__;
- J := Q.Hash mod NewCapacity;
- Q.FNext__ := THashable(NewList^[J]);
- NewList^[J] := Q;
- end;
- end;
- end
- else
- NewList := nil;
- if (FBucket <> nil) then FreeMem(FBucket);
- FBucket := NewList;
- FCapacity := NewCapacity;
- end;
- { THashMapIterator }
- constructor THashMapIterator.Create(AHashMap: THashMap);
- begin
- FHashMap := AHashMap;
- FIndex := -1;
- FNode := nil;
- FBefore := nil;
- FReread := False;
- end;
- procedure THashMapIterator.First;
- begin
- FIndex := -1;
- FNode := nil;
- FBefore := nil;
- FReread := False;
- end;
- function THashMapIterator.Next: TObject;
- begin
- if (FReread) then
- begin
- FReread := False;
- end
- else begin
- if (FNode <> nil) then
- begin
- FBefore := FNode;
- FNode := FNode.FNext__;
- end;
- if (FNode = nil) then
- begin
- Inc(FIndex);
- while ((FIndex < FHashMap.Capacity) and (FHashMap.FBucket^[FIndex] = nil)) do
- Inc(FIndex);
- if (FIndex < FHashMap.Capacity) then
- FNode := THashable(FHashMap.FBucket^[FIndex])
- else
- FNode := nil;
- FBefore := nil;
- end;
- end;
- Result := FNode;
- end;
- procedure THashMapIterator.Delete;
- var
- P: THashable;
- begin
- if (FNode <> nil) then
- begin
- P := FNode;
- if (FBefore <> nil) then
- FBefore.FNext__ := P.FNext__
- else
- FHashMap.FBucket^[FIndex] := P.FNext__;
- FNode := FNode.FNext__;
- if (FNode = nil) then
- begin
- Inc(FIndex);
- while ((FIndex < FHashMap.Capacity) and (FHashMap.FBucket^[FIndex] = nil)) do
- Inc(FIndex);
- if (FIndex < FHashMap.Capacity) then
- FNode := THashable(FHashMap.FBucket^[FIndex])
- else
- FNode := nil;
- FBefore := nil;
- end;
- FReread := True;
- P.Free;
- end;
- end;
- { TNamedObject }
- function TNamedObject.GetName: String;
- begin
- Result := FName;
- end;
- procedure TNamedObject.SetName(const Value: string);
- begin
- FName := Value;
- FHash := HashName(Value);
- end;
- function TNamedObject.IsEqual(AKey: Pointer): Boolean;
- begin
- Result := SameText(FName, String(AKey));
- end;
- function HashName(const AName: string): integer;
- var
- I: integer;
- C: Byte;
- begin
- Result := 0;
- for I := 1 to Length(AName) do
- begin
- C := Byte(AName[I]);
- if ((C >= Ord('a')) and (C <= Ord('z'))) then Dec(C,Ord('a')-Ord('A'));
- Result := ((Result SHL 5) OR (Result AND $1F)) + C;
- end;
- Result := Result AND $7FFFFFFF;
- end;
- type
- { TNamedObject }
- TNamedObject = class(THashable)
- private
- FName: string;
- protected
- function GetName: String;
- procedure SetName(const Value: string); virtual;
- public
- function IsEqual(AKey: Pointer): Boolean; override;
- property Name: string read GetName write SetName;
- end;
- { TNamedObjectMap }
- TNamedObjectMap = class(THashMap)
- protected
- function HashKey(AKey: Pointer): Integer; override;
- public
- function GetObject(const AName: String): TNamedObject;
- procedure Remove(const AName: string);
- end;
- { TNamedObject }
- function TNamedObject.GetName: String;
- begin
- Result := FName;
- end;
- procedure TNamedObject.SetName(const Value: string);
- begin
- FName := Value;
- FHash := HashName(Value);
- end;
- function TNamedObject.IsEqual(AKey: Pointer): Boolean;
- begin
- Result := SameText(FName, String(AKey));
- end;
- { TNamedObjectMap }
- function TNamedObjectMap.HashKey(AKey: Pointer): Integer;
- begin
- Result := HashName(String(AKey));
- end;
- function TNamedObjectMap.GetObject(const AName: String): TNamedObject;
- begin
- Result := TNamedObject(inherited GetObject(Pointer(AName)));
- end;
- procedure TNamedObjectMap.Remove(const AName: string);
- begin
- inherited Remove(Pointer(AName));
- end;
This is an example of name and row index search helper using Hash Map:
- type
- { TNameIndexItem }
- TNameIndexItem = class(THashable)
- private
- FKey: String;
- FRow: Integer;
- FData: Pointer;
- public
- constructor Create(const AKey: String; ARow: Integer; AData: Pointer=nil);
- function IsEqual(AKey: Pointer): Boolean; override;
- property Key: String read FKey;
- property Row: Integer read FRow;
- property Data: Pointer read FData;
- end;
- { TNameIndex }
- TNameIndex = class(THashMap)
- protected
- function HashKey(AKey: Pointer): Integer; override;
- public
- function GetObject(const AKey: String): TNameIndexItem;
- procedure Remove(const AKey: String);
- procedure PutRow(const AKey: String; ARow: Integer; AData: Pointer=nil);
- function FindRow(const AKey: String): Integer;
- function GetRowAndData(const AKey: String; var ARow: Integer;
- var AData: Pointer): Boolean;
- // procedure IndexTable(ATable: ITransportTable; const AColName: String);
- end;
- { TNameIndexItem }
- constructor TNameIndexItem.Create(const AKey: String; ARow: Integer;
- AData: Pointer);
- begin
- FKey := AKey;
- FHash := HashName(AKey);
- FRow := ARow;
- FData := AData;
- end;
- function TNameIndexItem.IsEqual(AKey: Pointer): Boolean;
- begin
- Result := SameText(FKey, String(AKey));
- end;
- { TNameIndex }
- function TNameIndex.FindRow(const AKey: String): Integer;
- var
- P: TNameIndexItem;
- begin
- P := GetObject(AKey);
- if (P <> nil) then
- Result := P.Row
- else
- Result := -1;
- end;
- function TNameIndex.GetRowAndData(const AKey: String; var ARow: Integer;
- var AData: Pointer): Boolean;
- var
- P: TNameIndexItem;
- begin
- P := GetObject(AKey);
- if (P <> nil) then
- begin
- ARow := P.Row;
- AData := P.Data;
- Result := True;
- end
- else
- Result := False;
- end;
- function TNameIndex.GetObject(const AKey: String): TNameIndexItem;
- begin
- Result := TNameIndexItem(inherited GetObject(Pointer(AKey)));
- end;
- function TNameIndex.HashKey(AKey: Pointer): Integer;
- begin
- Result := HashName(String(AKey));
- end;
- //procedure TNameIndex.IndexTable(ATable: ITransportTable;
- // const AColName: String);
- //var
- // i, col: Integer;
- //begin
- // if (ATable = nil) then
- // exit;
- // col := ATable.FindField(AColName);
- // if (col < 0) then
- // exit;
- // Clear;
- // for i := 0 to ATable.RowCount - 1 do
- // PutRow(ATable.Cells[col,i].AsString, i);
- //end;
- procedure TNameIndex.PutRow(const AKey: String; ARow: Integer;
- AData: Pointer);
- begin
- if (GetObject(AKey) = nil) then
- begin
- Put(TNameIndexItem.Create(AKey, ARow, AData));
- end;
- end;
- procedure TNameIndex.Remove(const AKey: String);
- begin
- inherited Remove(Pointer(AKey));
- end;
2. Hash Marking
- function TChain.GetObject(AKey: Pointer): THashable;
- var
- P: THashable;
- H: Integer;
- begin
- P := FFirst;
- H := HashKey(AKey);
- while ((P <> nil) and ((P.Hash <> H) or not P.IsEqual(AKey))) do
- P := P.FNext__;
- Result := P;
- end;
3. Binary Search
- function TIntegerList.IndexOfB(AItem: Integer): Integer;
- var
- Low,High,Mid: Integer;
- begin
- Result := -1;
- Low := 0;
- High := Count - 1;
- Mid := (Low + High) div 2;
- while (Low <= High) do
- begin
- if (Items[Mid] > AItem) then High := Mid - 1
- else if (Items[Mid] < AItem) then Low := Mid + 1
- else begin
- Result := Mid;
- exit;
- end;
- Mid := (Low + High) div 2;
- end;
- end;
Selasa, 16 Desember 2025
Creating Expert System for Database Application Development
The design of Gampang Builder is very simple, it get input from the user about the application design and generate software project for it, not a complete software but only:
- Main form and main menu of the application.
- Navigation system to access facilities in the application and limiting access by application user.
- Data service for create, edit and delete for entities and relations.
- Editing form for the entites and relations.
- Printable report for the entities.
When we create an application first we have to define the backgroud of the problem to solve and the target to be achieved by the application.
Then we choose the technology for the to be generated application.
To make our code more reusable I split the code into few modules.In each module we can define the class name for the data service and access right than can be used to limit user access because not every user will have the same access level.After we define the modules we can define the entities by the data table and relations between entities in the applications.
In the data tabel we can define field name, data type, its characteristic, and relation with other table such as look up or master-detail.If we need the printed design for documentation, Gampang Builder can print it.
The last thing to do is generate the application but because this application is too old and many changes to the library change the package file reader, I will repair the code and update when it completed. I created a few application with this program, one of the application is a medical laboratory database application and is still running today from 2016. I dont have time yet to improve this program but maybe someday.
UPDATE: Yesterday I have a requirement to generate a flutter Application so I have to fix the Gampang Builder before adding flutter application code generation module. So I open this project again and fix the problem with my modified library. And the problem is TTransportVar object using different format when storing String data. After I fix the file packer object with the modified library then everything is working again and the code generation is back to normal.
This is the generated folder for the middle ware of an Lazarus Application:
And this is the generated folder for the client app:
This is one of the server module API code:
This is one of the client save edit procedure:
It is very simple but the generated code compiled without any errors and easy to add code later.
That's all in this article, I hope it's usefull.
Creating Simple Web Server with Lazarus
Web server is a program or sub program that provide data to client program not limited to web browser using HTTP or HTTPS (HTTP over SSL) protocol. HTTP is a simple, fast and robust protocol for client-server application. The HTTP protocol works by having the client send a request to the server, then the server processes the request and sends back a response to the client.
Example of HTTP Client's Request:
GET / HTTP/1.1 Host: www.example.com
The first line of a HTTP request consist of a command, a url and the protocol version. Followed by header lines and terminated by empty line. Each header line consist of header name and value separated by colon and one space. After the header data is the content data that contain n bytes where n is the value of Content-Length header.
Example of HTTP Server's Response:
HTTP/1.1 200 OK Date: Mon, 23 May 2005 22:38:34 GMT Content-Type: text/html; charset=UTF-8 Content-Length: 155 Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux) ETag: "3f80f-1b6-3e1cb03b" Accept-Ranges: bytes Connection: close <html> <head> <title>An Example Page</title> </head> <body> <p>Hello World, this is a very simple HTML document.</p> </body> </html>
The first line of a HTTP response consist of protocol version and result code followed by error message. Followed by header lines and terminated by empty line. Each header line consist of header name and value separated by colon and one space. After the header data is the content data that contain n bytes where n is the value of Content-Length header.
Free Pascal, Delphi and Lazarus share the same programming language the Object Pascal with little differentiation. So many Delphi component can be used in Lazarus and vice versa because it only requires minor changes in the source code to support both programming. If you don't want to use visual component you can use operating system API like winsock (Windows Socket) for Windows or unisock (Unix Socket) for Linux. I will use synapse for Free Pascal library in this example.
To help parsing input data I create TStringSplitter :
- type
- { TStringSplitter }
- TStringSplitter = class(TObject)
- private
- FLine: String;
- FCol: Integer;
- procedure SetCol(AValue: Integer);
- procedure SetLine(AValue: String);
- public
- constructor Create;
- function Fetch(const ATerminator: String=''): String;
- property Line: String read FLine write SetLine;
- property Col: Integer read FCol write SetCol;
- end;
- function PosAfter(const SubText, Text: String; StartAt: Integer): Integer;
- var
- I, L1, L2: Integer;
- begin
- Result := -1;
- L1 := Length(SubText);
- L2 := Length(Text);
- if ((L1 > L2) or (StartAt < 1)) then exit;
- for I := StartAt to L2-L1+1 do
- if (CompareMem(@SubText[1], @Text[I], L1)) then
- begin
- Result := I;
- exit;
- end;
- end;
- procedure TStringSplitter.SetCol(AValue: Integer);
- begin
- FCol := AValue;
- end;
- procedure TStringSplitter.SetLine(AValue: String);
- begin
- FLine := AValue;
- FCol := 1;
- end;
- constructor TStringSplitter.Create;
- begin
- FLine := '';
- FCol := 1;
- end;
- function TStringSplitter.Fetch(const ATerminator: String): String;
- var
- I, J: Integer;
- begin
- Result := '';
- I := FCol;
- if (I > Length(FLine)) then exit;
- if (ATerminator <> '') then
- begin
- J := PosAfter(ATerminator, FLine, I);
- if (J > 0) then
- begin
- Result := Copy(FLine, I, J-I);
- FCol := J + Length(ATerminator);
- exit;
- end;
- end;
- Result := Copy(FLine, FCol, MaxInt);
- FCol := Length(FLine) + 1;
- end;
When the server is started it initialize the socket to listen to the specified port. Then it repeatly check for incoming connection and start HTTP handler worker thread until the server is terminated.
- procedure TDaemon1.WorkServerListener(Thread: TWorkerThread);
- var
- sock: TTCPBlockSocket;
- h: TSocket;
- begin
- try
- sock := TTCPBlockSocket.Create;
- try
- sock.Bind('0.0.0.0',IntToStr(Port));
- if (sock.LastError <> 0) then
- raise ENetworkError.Create('Cannot bind to port '+IntToStr(Port));
- sock.Listen;
- if (sock.LastError <> 0) then
- raise ENetworkError.Create('Cannot listen to port '+IntToStr(Port));
- while (not Thread.Terminated) do
- begin
- if (sock.CanRead(5000)) then
- begin
- h := sock.Accept;
- {$HINTS OFF}
- if (h <> INVALID_SOCKET) then
- WorkerThreads.AddNew(@WorkRequestHandler,Pointer(h));
- {$HINTS ON}
- end;
- end;
- finally
- sock.Free; // close and free socket object
- end;
- except
- on E: Exception do
- // DebugLog('HTTP Server terminated by error '+E.ClassName+': '+
- // E.Message);
- end;
- end;
The HTTP handler worker thread read HTTP request from incoming connection and send back HTTP Respond.
- procedure TDaemon1.WorkRequestHandler(Thread: TWorkerThread);
- var
- sock: TTCPBlockSocket;
- S1: TStringSplitter;
- s, cmd, url, http_ver, nm, vl, error_msg, respond_text, respond_data: AnsiString;
- I, result_code: Integer;
- close_socket, can_read: Boolean;
- headers, gets, respond_headers: TStrings;
- begin
- result_code := 500;
- close_socket := True;
- sock := TTCPBlockSocket.Create;
- S1 := TStringSplitter.Create;
- headers := TStringList.Create;
- gets := TStringList.Create;
- respond_headers := TStringList.Create;
- try
- {$HINTS OFF}
- sock.Socket := TSocket(Thread.Param);
- {$HINTS ON}
- repeat
- can_read := sock.CanRead(5000);
- if (sock.LastError <> 0) then exit;
- if (can_read) then
- begin
- I := Pos('?',url);
- if (I >= 1) then
- begin
- S1.Line := Copy(url, I+1, MaxInt);
- repeat
- nm := S1.Fetch('=');
- if (nm <> '') then
- begin
- vl := S1.Fetch('&');
- gets.Values[nm] := vl;
- end;
- until (nm = '');
- Delete(url,I,MaxInt);
- end;
- S1.Line := sock.RecvString(ReadTimeout);
- if (sock.LastError <> 0) then exit;
- cmd := S1.Fetch(' ');
- url := S1.Fetch(' ');
- http_ver := S1.Fetch;
- if ((cmd = 'GET') or (cmd = 'POST')) then
- begin
- close_socket := (http_ver = 'HTTP/0.9') or (http_ver = 'HTTP/1.0');
- S1.Line := sock.RecvString(ReadTimeout);
- if (sock.LastError <> 0) then exit;
- while (S1.Line <> '') do
- begin
- nm := S1.Fetch(': ');
- vl := S1.Fetch;
- headers.Values[nm] := vl;
- S1.Line := sock.RecvString(ReadTimeout);
- if (sock.LastError <> 0) then exit;
- end;
- //
- // .. do some processing here
- //
- respond_data :=
- '<head><title>Welcome</title></head><body><h1>Welcome to my Web Server</h1><p>You are in here: '+HttpEncode(url)+'</body>';
- respond_headers.Values['Content-Type'] := 'html/text';
- respond_headers.Values['Content-Length'] := IntToStr(Length(respond_data));
- result_code := 200;
- end
- else begin
- result_code := 501;
- close_socket := True;
- end;
- if((result_code < 200) or (result_code > 499)) then close_socket := True;
- case result_code of
- 200: error_msg := 'OK';
- 400: error_msg := 'Bad Reqeust';
- 500: error_msg := 'Internal Server Error';
- 501: error_msg := 'Not Implemented';
- end;
- respond_text :=
- http_ver + ' ' + IntToStr(result_code) + ' ' + error_msg + #13#10;
- for I := 0 to respond_headers.Count do
- respond_text := respond_text + respond_headers.Names[I] + ': ' +
- respond_headers.ValueFromIndex[I] + #13#10;
- respond_text := respond_text + #13#10 + respond_data;
- sock.SendBuffer(@respond_text[1], Length(respond_text));
- headers.Clear;
- respond_headers.Clear;
- respond_text := '';
- end;
- until (Thread.Terminated or close_socket);
- finally
- respond_headers.Free;
- gets.Free;
- headers.Free;
- S1.Free;
- sock.Free;
- end;
- end;
You can send and receive data using easy to parse data like JSON, XML, BSON or binary data for data service, it is very flexible.
That's all for this article and I hope it's useful.
Performa AMD Ryzen 5 3400G iGPU dengan DDR4 3200 MHz untuk Llama.cpp
Bulan ini banyak model LLM sangat bagus yang diluncurkan dan ada beberapa model yang free dan bisa dijalankan di komputer rumah. Untuk menja...
-
Daemon Application in Linux or Service Application in Windows is an application that running in the background, usually automatically starte...
-
In the year 2016, my friend show me he create a desktop application using Delphi that he create very fast. I think I cannot compete with him...
-
Thread in Free Pascal and Delphi is a process that run simutanously with main program. This is very useful when we need to process data in...





















