PEJI.IR

یک json parser ساده با c

۱۳۹۸ تیر ۲۷, پنجشنبه ساعت ۱۶:۰۳

چند روزی هست که دارم خودم رو با زبان c به چالش میکشم و سعی میکنم کارهایی که قبلا با c++ انجام دادم رو با c انجام بدم.

یکی از اون کارها نوشتن یک json parser هست که بتونه همه ی حالت های json رو پردازش کنه و بشه از اون dump گرفت. مدیریت کردن Ram در زبان c کمی حوصله میخواد و اگر جایی رو حواست نباشه ممکنه memory leak داشته باشیم. برای همین تمام برنامه ها رو با valgrind چک میکنم که جایی memory leak نداشتم باشم.

لینک پروژه :

https://github.com/pejman-hkh/c-json-parser

نمونه dump :

[boolean : true]
[color : #82b92c]
[null : null]
[number : 123]
[object : 
[a : b]
[c : d]
[e : f]]
[string : HelloWorld]
[array : 
[0 : 1]
[1 : 2]
[2 : 3]]
[boolean1 : true]
[color1 : #82b92c]
[null1 : null]
[number1 : 123]
[object1 : 
[a : b]
[c : d]
[e : f]]
[string1 : HelloWorld]
[array1 : 
[0 : 1]
[1 : 2]
[2 : 3]]

یک threadpool ساده با c

۱۳۹۸ تیر ۲۷, پنجشنبه ساعت ۱۵:۵۴

ایجاد کردن تعداد زیادی thread باعث اشغال شدن ظرفیت کل cpu میشود. برای همین باید ایجاد کردن thread به صورت سازمندهی شده انجام بشه و تنها از ظرفیتی که سیستم داره استفاده کرد. برای همین از استخر thread استفاده میکنیم که پیاده سازی اون هم مستلزم ایجاد Queue و Task هست. قبل از اینکه thread ها رو ایجاد کنم یک Queue و Task خیلی ساده نوشتم که میشه داخل Queue مون Task هامون رو اضافه کنیم و بعد هر جایی خواستیم اون رو فراخونی کنیم.

https://github.com/pejman-hkh/c-queue-task

حالا میشه تمام Task ها رو به Queue اضافه کنیم و داخل چند Thread مجزا از Queue مون یک Task رو برداریم و اون رو اجرا کنیم. برای اینکه بشه از Queue مون همزمان توی چند تا Thread مختلف Task ئی رو برداریم از Mutex استفاده کردم و همچنین اگر صفمون خالی بود با Condition صف رو نگه داشتم که به رم و سی پیو فشاری نیاد...

https://github.com/pejman-hkh/c-threadpool-worker-job

لینک پروژه ها :

https://github.com/pejman-hkh/c-queue-task

https://github.com/pejman-hkh/c-threadpool-worker-job

phpoop

۱۳۹۷ بهمن ۲۵, پنجشنبه ساعت ۲۰:۰۱

https://github.com/pejman-hkh/phpOop

این ایده به ذهنم رسید که بهتره توابع php همشون توی یک کلاس باشن و بشه ازشون پشت سر هم استفاده کرد. در این روش شیوه کد نویسی هم راحتتر میشه. به جای نوشتن تابع های تو در تو میشه پشت سر هم عملیات رو روی متغییرمون که میتونه آرایه و یا هر چیزی باشه انجام داد.

پروژه رو با Zephir lang هم بازنویسی کردم. ( برای Performance بهتر و ... )

مشکل زیرنویس فارسی

۱۳۹۷ دی ۱۳, پنجشنبه ساعت ۱۵:۱۶

خیلی ها موقع فیلم دیدن با زیرنویس های خرچنگ قورباغه مواجه میشن، مثل یکی از دوستان من که هر بار زنگ میزنه و میگه این مشکل رو دارم. من یک فایل کوچیک html درست کردم که این مشکل را باهاش حل کنم. آدرس در وبلاگ خودم :

http://www.peji.ir/subtitle.html

دانلود فایل :

https://github.com/pejman-hkh/persian-subtitle

متن زیرنویس رو داخل اون Paste کنید و یا اینکه فایل رو Browse کنید تا به شما خروجی درست زیرنویس را بدهد.

Chrome bug orientation

۱۳۹۷ دی ۹, یکشنبه ساعت ۱۳:۴۲

مرورگر کروم برای عکس ها از تگهای Exif Data تگ Orientatoin رو در نظر نمیگیره. البته این باگی هست که همه ی مرورگرها دارن. فایر فاکس خاصیت image-orientation رو به Css اضافه کرده :

img {
    image-orientation: from-image;
}

ولی هنوز Chrome این رو اضافه نکرده ... به جای چرخوندن عکس و گذاشتن کلی وقت میشه یک تگ رو تغییر داد و به جاش عکس رو چرخوند ولی این استاندارد خیلی جاها هنوز اعمال نشده.

جمع با jit قسمت دوم

۱۳۹۷ آذر ۱۶, جمعه ساعت ۱۱:۳۵

لینک پروژه :

https://github.com/pejman-hkh/tiny-jit-compiler

قسمت قبل را خیلی وقت پیش نوشتم و وقت نشد که آنرا ادامه دهم. جمع با jit که تنها یک قسمت کوچک از کامپایل هست را اینبار با nodejs انجام دادم که خروجی assembly به ما میدهد. (کد را حذف کردم ) در جلسه قبل ما توابع کوچکی برای اسمبل کردن نوشتیم که به جای print از آن توابع استفاده میکنیم تا جمع و ضرب و تقسیم ما به صورت jit انجام شود.

باگ : اعداد منفی خیلی جاها اعمال نمیشود. ( برطرف شد )

نتیجه نهایی کار که با قسمت قبل کد ها تلفیق کردم و کمی هم ویرایش کردم تا ضرب و جمع و تقسیم و تفریق را با اولویت انجام بدهیم :

https://github.com/pejman-hkh/tiny-jit-compiler

تست هایی که انجام دادم

./a.out 4*-3-1*-20 => 8
./a.out -2*-3-1*-20 => 26
./a.out -2*5+1*-8 => -18

نمونه خروجی برنامه :

./a.out -2*5+1*-8
-2*5+1*-8
mov eax, 2
mov ecx, 0
sub eax, ecx
neg eax
push eax
mov eax, 5
pop ecx
imul eax, ecx
push eax
mov eax, 1
push eax
mov eax, 8
mov ecx, 0
sub eax, ecx
neg eax
pop ecx
imul eax, ecx
pop ecx
add eax, ecx
leave
ret
-18

اگر وقت داشته باشم و البته سر ذوق باشم و صد البته همچنان سر ذوق بمانم میخواهم BrainFuck را به صورت jit بنویسم.

منتظر قسمت سوم باشید ... ;)

اجرا کردن کد php با fpm

۱۳۹۷ آبان ۲۶, شنبه ساعت ۱۱:۰۹

اگر جایی لازم دارید که کاربر منتظر پردازش شما نباشد و کدتان را به اصطلاح در background اجرا کنید میتوانید با shell خود php را صدا بزنید و آدرس فایلتان را به آن بدهید تا فایلتان را در background اجرا کند. اما صدا زدن هر بار آن بار زیادی برای Cpu شما خواهد داشت. بهتر است که فایل php تان با بدهید به php-fpm تا آن را برایتان اجرا کند. برای اینکار ابتدا yum --enablerepo=epel install fcgi را نصب کنید و سپس از کد زیر استفاده کنید.

<?php
function runPhp( $path, $qs, $daemon = true ) {
	$cmd = 'SCRIPT_NAME=/'.basename( $path ).' \
	SCRIPT_FILENAME='.$path.' \
	QUERY_STRING='.$qs.' \
	DOCUMENT_ROOT='.__dir__.' \
	REQUEST_METHOD=GET \
	cgi-fcgi -bind -connect 127.0.0.1:9000 '.($daemon?'> /dev/null 2>/dev/null &':'').'';

    $ret = shell_exec( $cmd );
    if( ! $daemon )
        return explode("\r\n\r\n", $ret, 2)[1];];
}

echo runPhp( __dir__.'/test1.php', http_build_query([ 'id' => 10 ]), false );

?>

برده داری نوین یا همان کارمندی

۱۳۹۷ شهریور ۱۵, پنجشنبه ساعت ۱۵:۱۲

فیلم django unchained فیلم قشنگی بود. زمانی آدمها را ( نه هر آدمی، باید پوستش سیاه می بود ) مجبور به برده داری میکردند و اگر مهر بردگی روی پیشانیش میخورد تا آخر عمر باید برده میبود. الان آدمها را گرسنه نگه میدارند تا خودشان دنبال بردگی بروند. در سرزمین عزیزم ایران هیچگاه چیزی به اسم برده داری مرسوم نبوده اما باز هم فکر انگلیسی آمریکایی در ذهن عده ای رسوخ کرده و دارند به همان شیوه ۱۰۰ سال پیش آمریکایی ها برده داری میکنند. برده شان نباید از جایش تکان بخورد. باید کار کند و کار. دیگر مثل گذشته خبری از شلاق نیست و جایش را زبان و زخم زبان گرفته. زخم زبان به شیوه انگلیسی. آنقدر به کارمندشان استرس وارد میکنند که حال و هوای زندگی کردن از یادش میرود و مدام دارد به چیزهایی فکر میکند که اربابش به جان او انداخته است. برده یا همان کارمند باید بشیند سر جایش و فقط کار کند و کار.

جمع با jit

۱۳۹۷ فروردین ۱۴, سه‌شنبه ساعت ۲۰:۱۶

https://0xax.github.io/

آموزش خوبی برای assembly هست.

https://github.com/pejman-hkh/jit

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>

int exec( unsigned char *code, int size ) {

  void *mem = mmap(NULL, size, PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0);

  memcpy(mem, code, size);

  int (*func)() = mem;

  return func();
}

int main(int argc, char *argv[]) {
  // Machine code for:


  if (argc < 3) {
    fprintf(stderr, "Usage: ./a.out <integer> <integer>\n");
    return 1;
  }

  //   mov eax, 0
  //   mov ebx, 0
  //   add eax, ebx
  //   ret

  //x86 sum
  unsigned char code[] = {
    0xB8, atoi(argv[1]), 0x00, 0x00, 0x00, 
    0xBB, atoi(argv[2]), 0x00, 0x00, 0x00, 
    0x01, 0xD8, 
    0xC3 
  };

  printf("%d\n", exec( code, sizeof( code ) ) );

}

https://defuse.ca/online-x86-assembler.htm#disassembly

سایت خوبی هست برای تبدیل کدهای اسمبلی به دستورات cpu intel x86

یک مثال خیلی ساده برای جمع دو عدد.

فرض کنید که بخواهیم عملیات ریاضی زیر را انجام دهیم :

5+2*12/4+3

کد اسمبلی این عملیات به شکل زیر خواهد بود :

mov eax, 12
mov edx, 0
mov ebx, 4
div ebx
mov ebx, 2
mul ebx
mov ebx, 5
add eax, ebx
mov ebx, 3
add eax, ebx
ret

که در صورتی که با تابع exec آنرا اجرا کنیم کد به شکل زیر خواهد بود و جواب ۱۴ خواهد شد :

  unsigned char code[] = {
    0xB8, 0x0C, 0x00, 0x00, 0x00,

    0xBA, 0x00, 0x00, 0x00, 0x00,

    0xBB, 0x04, 0x00, 0x00, 0x00,

    0xF7, 0xF3,

    0xBB, 0x02, 0x00, 0x00, 0x00,

    0xF7, 0xE3,

    0xBB, 0x05, 0x00, 0x00, 0x00,

    0x01, 0xD8,

    0xBB, 0x03, 0x00, 0x00, 0x00,

    0x01, 0xD8, 

    0xC3
  };
  
  printf("%d\n", exec( code, sizeof( code ) ) );
  

خواندن و تبدیل کردن کد بالا به کد اسمبلی و اسمبل کردن کد کاری هست که من میخواهم آنرا انجام دهم

یک جمع پیشرفته تر را در نظر بگیرید. اینجا میخواهم از stack استفاده کنم :

4
+
2*3*5
+
6*7
+
4*1

کد اسمبلی این جمع به شکل زیر خواهد بود :

mov eax, 2
mov ebx, 3
mul ebx
mov ebx, 5
mul ebx
push eax

mov eax, 6
mov ebx, 7
mul ebx
push eax

mov eax, 4
mov ebx, 1
mul ebx
push eax

pop eax
pop ebx
add eax,ebx
pop ebx
add eax, ebx


mov ebx,4
add eax, ebx

هر بار که ضرب و تقسیم انجام میدهم مقدار eax را در stack push میکنم و برای جمع کردن همه ی مقدارها را به ترتیب pop میکنم و جمع و یا تفریق را انجام میدهم.

بایت کد، کد اسمبلی بالا به شکل زیر خواهد بود :

0:  b8 02 00 00 00          mov    eax,0x2
5:  bb 03 00 00 00          mov    ebx,0x3
a:  f7 e3                   mul    ebx
c:  bb 05 00 00 00          mov    ebx,0x5
11: f7 e3                   mul    ebx
13: 50                      push   eax
14: b8 06 00 00 00          mov    eax,0x6
19: bb 07 00 00 00          mov    ebx,0x7
1e: f7 e3                   mul    ebx
20: 50                      push   eax
21: b8 04 00 00 00          mov    eax,0x4
26: bb 01 00 00 00          mov    ebx,0x1
2b: f7 e3                   mul    ebx
2d: 50                      push   eax
2e: 58                      pop    eax
2f: 5b                      pop    ebx
30: 01 d8                   add    eax,ebx
32: 5b                      pop    ebx
33: 01 d8                   add    eax,ebx
35: bb 04 00 00 00          mov    ebx,0x4
3a: 01 d8                   add    eax,ebx
3c: c3                      ret

جواب معادله بالا مقدار ۸۰ خواهد بود. برای اینکار میتوانید کد زیر را اجرا کنید :

  unsigned char code[] = {
    0xB8, 0x02, 0x00, 0x00, 0x00, 0xBB, 0x03, 0x00, 0x00, 0x00, 0xF7, 0xE3, 0xBB, 0x05, 0x00, 0x00, 0x00, 0xF7, 0xE3, 0x50, 0xB8, 0x06, 0x00, 0x00, 0x00, 0xBB, 0x07, 0x00, 0x00, 0x00, 0xF7, 0xE3, 0x50, 0xB8, 0x04, 0x00, 0x00, 0x00, 0xBB, 0x01, 0x00, 0x00, 0x00, 0xF7, 0xE3, 0x50, 0x58, 0x5B, 0x01, 0xD8, 0x5B, 0x01, 0xD8, 0xBB, 0x04, 0x00, 0x00, 0x00, 0x01, 0xD8, 0xC3
  };

  printf("%d\n", exec( code, sizeof( code ) ) );

اگر قرار باشد که جمع بالا را انجام دهم یکبار bytecode آنرا ذخیره میکنم و دفعه بعد فقط آنرا روی رم مینویسم و خروجی آنرا میگیرم. این دقیقا همان کاری است که زبان های jit انجام میدهند. زبانی مثل جاوا کد شما را به bytecode خودش تبدیل میکند و دفعه بعد فقط آنرا به bytecode مورد نظر cpu که مثلا x86 و یا x64 هست تبدیل میکند و آنرا روی رم مینویسد و اجرا میکند. پیشتر که رفتیم سعی میکنم که یک متغییر تعریف کنم و به آن مقدار دهم تا کمی پیشرفته تر کار کنیم.

اگر بخواهیم کد بالا را به کد c تبدیل کنیم به شکل زیر در خواهد آمد :


  mov( EAX, 2 );
  mov( EBX, 3 );
  mul( EBX );
  mov( EBX, 5 );
  mul( EBX );  
  push( EAX );


  mov( EAX, 6 );
  mov( EBX, 7 );
  mul( EBX );
  push( EAX );

  mov( EAX, 4 );
  mov( EBX, 1 );
  mul( EBX );
  push( EAX );

  pop( EAX );
  pop( EBX );
  add( EAX, EBX );
  pop( EBX );
  add( EAX, EBX );

  mov( EBX, 4 );
  add( EAX, EBX );
  ret();

خروجی این کد باید bytecode بالا باشد.

برای اجرای کد بالا من چند تا تابع کوچیک ناقص نوشتم که کد مارو میسازه و باز خروجی ما جمعش ۸۰ خواهد شد :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <stdlib.h>


int exec( unsigned char *code, int size ) {

  void *mem = mmap(NULL, size, PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0);

  memcpy(mem, code, size);

  int (*func)() = mem;

  return func();
}

enum {
  EAX,
  EBX,
  ECX,
  EDX,
};

unsigned char asm_instruction[1000];
int asm_iter = 0;
void set_asm( unsigned char * ret, int size ) {
  for( int i = 0; i < size; i++ ) {
    asm_instruction[asm_iter++] = ret[i];
  } 
}

void mov( int reg, int val ) {
  unsigned char ret[5];

  switch( reg ) {
    case EAX :
      ret[0] = 0xB8;
      ret[1] = val;
      ret[2] = 0x00;
      ret[3] = 0x00;
      ret[4] = 0x00;
      break;

    case EBX : 
      ret[0] = 0xBB;
      ret[1] = val;
      ret[2] = 0x00;
      ret[3] = 0x00;
      ret[4] = 0x00;

      break;

    case EDX :
      ret[0] = 0xBA;
      ret[1] = val;
      ret[2] = 0x00;
      ret[3] = 0x00;
      ret[4] = 0x00;
      break;
  }


  set_asm( ret, sizeof( ret ) );
}

void push( int reg ) {
  unsigned char ret[1];
  switch( reg ) {
    case EAX :
      ret[0] = 0x50;
      break;
    case EBX :
      ret[0] = 0x53;
      break;
  }

  set_asm( ret, sizeof( ret ) );
}

void add( int reg, int reg1 ) {
  unsigned char ret[2];
  switch( reg ) {
    case EAX :
      ret[0] = 0x01;
      switch( reg1 ) {
        case EBX :
          ret[1] = 0xD8;
          break;
      }
      break; 
  }

  set_asm( ret, sizeof( ret ) );
}

void mul( int reg ) {
  unsigned char ret[2];
  switch( reg ) {
    case EAX :
      ret[0] = 0xF7;
      ret[1] = 0xE0;
      break;
    case EBX :
      ret[0] = 0xF7;  
      ret[1] = 0xE3;
      break;
  }

  set_asm( ret, sizeof( ret ) );
}

void pop( int reg ) {
  unsigned char ret[1];

  switch( reg ) {
    case EAX :
      ret[0] = 0x58;
      break;
    case EBX :
      ret[0] = 0x5B;
      break; 
  }

  set_asm( ret, sizeof( ret ) );

}

void ret() {
  unsigned char ret[1];
  ret[0] = 0xC3;
  set_asm( ret, sizeof( ret ) );
}

int main(int argc, char *argv[]) {

  mov( EAX, 2 );
  mov( EBX, 3 );
  mul( EBX );
  mov( EBX, 5 );
  mul( EBX );  
  push( EAX );


  mov( EAX, 6 );
  mov( EBX, 7 );
  mul( EBX );
  push( EAX );

  mov( EAX, 4 );
  mov( EBX, 1 );
  mul( EBX );
  push( EAX );

  pop( EAX );
  pop( EBX );
  add( EAX, EBX );
  pop( EBX );
  add( EAX, EBX );

  mov( EBX, 4 );
  add( EAX, EBX );
  ret();

  /*for( int i = 0; i < asm_iter; i++ ) {
    printf("%x ", asm_instruction[i] ); 
  }*/

  printf("%d\n", exec( asm_instruction, asm_iter ) );

  return 0;
}

از کد بالا میشه به عنوان یک assembler خیلی خیلی ساده استفاده کرد و اون را حتی develope کرد برای نوشتن مابقی دستوراتمون.

https://github.com/pejman-hkh/tiny-jit-assembler

با استفاده از کد بالا میشه خیلی راحت حداقل یک معادله ساده ریاضی رو به روش jit انجام داد.

اول از همه باید کد رو parsing کرد و بعد ابتدا ضرب و تقسیم ها رو انجام داد و همه رو push کرد داخل stack و بعد یکی یکی اونا رو pop کرد و جمع و یا تفریق کنیم و در آخر اون رو ret کنیم.

روش نهایی برای جمع بالا

//push all in stack
  mov ( EAX, 4);
  push( EAX );

  mov( EAX, 2 );
  mov( EBX, 3 );
  mul( EBX );
  mov( EBX, 5 );
  mul( EBX );  
  push( EAX );


  mov( EAX, 6 );
  mov( EBX, 7 );
  mul( EBX );
  push( EAX );

  mov( EAX, 4 );
  mov( EBX, 1 );
  mul( EBX );
  push( EAX );


//pop first number
  pop( EAX );

//sum all numbers
  pop( EBX );
  add( EAX, EBX );

  pop( EBX );
  add( EAX, EBX );

  pop( EBX );
  add( EAX, EBX );

//ret result
  ret();