implemented new code splitting mechanism (split to equal slices)
[deliverable/titan.core.git] / compiler2 / CodeGenHelper.cc
index 772c5d54222d1d460d0d27195f28f5f1eb8829ab..5461368e977c6284f3cd032842a050e2569e9572 100644 (file)
@@ -9,6 +9,7 @@
  *   Balasko, Jeno
  *   Baranyi, Botond
  *   Raduly, Csaba
+ *   Szabo, Bence Janos
  *   Zalanyi, Balazs Andor
  *
  ******************************************************************************/
@@ -20,7 +21,7 @@
 #include <cstring>
 
 namespace Common {
-
+    
 CodeGenHelper* CodeGenHelper::instance = 0;
 
 CodeGenHelper::generated_output_t::generated_output_t() :
@@ -100,7 +101,8 @@ const char* const CodeGenHelper::typetypemap[] = {
 };
 
 CodeGenHelper::CodeGenHelper() :
-  split_mode(SPLIT_NONE)
+  split_mode(SPLIT_NONE),
+  slice_num(1)
 {
   if (instance != 0)
     FATAL_ERROR("Attempted to create a second code generator.");
@@ -115,14 +117,38 @@ CodeGenHelper& CodeGenHelper::GetInstance() {
 
 void CodeGenHelper::set_split_mode(split_type st) {
   split_mode = st;
+  
+  if (split_mode == SPLIT_TO_SLICES) {
+    split_to_slices = true;
+  } else {
+    split_to_slices = false;
+  }
 }
 
 bool CodeGenHelper::set_split_mode(const char* type) {
-  if (strcmp(type, "none") == 0)
+  int n;
+  if (strcmp(type, "none") == 0) {
     split_mode = SPLIT_NONE;
-  else if (strcmp(type, "type") == 0)
+    split_to_slices = false;
+  } else if (strcmp(type, "type") == 0) {
     split_mode = SPLIT_BY_KIND;
-  else
+    split_to_slices = false;
+  } else if ((n = atoi(type))) {
+    size_t length = strlen(type);
+    for (int i=0;i<length; i++) {
+      if (!isdigit(type[i])) {
+        ERROR("The number argument of -U must be a valid number.");
+        break;
+      }
+    }
+    split_mode = SPLIT_TO_SLICES;
+    if (n < 1 || n > 999999) {
+      ERROR("The number argument of -U must be between 1 and 999999.");
+      return false;
+    }
+    slice_num = n;
+    split_to_slices = slice_num > 1; // slice_num == 1 has no effect
+  } else
     return false;
   return true;
 }
@@ -155,6 +181,128 @@ void CodeGenHelper::set_current_module(const string& name) {
   current_module = name;
 }
 
+void CodeGenHelper::update_intervals(output_struct* const output) {
+  if(instance->split_mode != SPLIT_TO_SLICES) return;
+  
+  size_t tmp;
+  
+  // 1. check if some characters are added to the charstring
+  // 2. increment size variable
+  // 3. if size is bigger than the array's size, then double the array size
+  // 4. store new end position
+  
+  // class_defs are not counted as they will be in the header
+  tmp = mstrlen(output->source.function_bodies);
+  if (output->intervals.function_bodies[output->intervals.function_bodies_size] < tmp) {
+    output->intervals.function_bodies_size++;
+    if (output->intervals.function_bodies_size > output->intervals.function_bodies_max_size) {
+      output->intervals.function_bodies_max_size *= 2;
+      output->intervals.function_bodies = (size_t*)Realloc(output->intervals.function_bodies, output->intervals.function_bodies_max_size * sizeof(size_t));
+    }
+    output->intervals.function_bodies[output->intervals.function_bodies_size] = tmp;
+  }
+  tmp = mstrlen(output->source.methods);
+  if (output->intervals.methods[output->intervals.methods_size] < tmp) {
+    output->intervals.methods_size++;
+    if (output->intervals.methods_size > output->intervals.methods_max_size) {
+      output->intervals.methods_max_size *= 2;
+      output->intervals.methods = (size_t*)Realloc(output->intervals.methods, output->intervals.methods_max_size * sizeof(size_t));
+    }
+    output->intervals.methods[output->intervals.methods_size] = tmp;
+  }
+  tmp = mstrlen(output->source.static_conversion_function_bodies);
+  if (output->intervals.static_conversion_function_bodies[output->intervals.static_conversion_function_bodies_size] < tmp) {
+    output->intervals.static_conversion_function_bodies_size++;
+    if (output->intervals.static_conversion_function_bodies_size > output->intervals.static_conversion_function_bodies_max_size) {
+      output->intervals.static_conversion_function_bodies_max_size *= 2;
+      output->intervals.static_conversion_function_bodies = (size_t*)Realloc(output->intervals.static_conversion_function_bodies, output->intervals.static_conversion_function_bodies_max_size * sizeof(size_t));
+    }
+    output->intervals.static_conversion_function_bodies[output->intervals.static_conversion_function_bodies_size] = tmp;
+  }
+  tmp = mstrlen(output->source.static_function_bodies);
+  if (output->intervals.static_function_bodies[output->intervals.static_function_bodies_size] < tmp) {
+    output->intervals.static_function_bodies_size++;
+    if (output->intervals.static_function_bodies_size > output->intervals.static_function_bodies_max_size) {
+      output->intervals.static_function_bodies_max_size *= 2;
+      output->intervals.static_function_bodies = (size_t*)Realloc(output->intervals.static_function_bodies, output->intervals.static_function_bodies_max_size * sizeof(size_t));
+    }
+    output->intervals.static_function_bodies[output->intervals.static_function_bodies_size] = tmp;
+  }
+}
+//Advised to call update_intervals before this
+size_t CodeGenHelper::size_of_sources(output_struct * const output) {
+  size_t size = 0;
+  // Calculate global var and string literals size
+  output->intervals.pre_things_size = mstrlen(output->source.global_vars) + mstrlen(output->source.string_literals);
+  
+  // Class_defs, static_conversion_function_prototypes, static_function_prototypes are in the header,
+  // and includes are not counted
+  size = output->intervals.pre_things_size +
+         output->intervals.function_bodies[output->intervals.function_bodies_size] +
+         output->intervals.methods[output->intervals.methods_size] +
+         output->intervals.static_conversion_function_bodies[output->intervals.static_conversion_function_bodies_size] +
+         output->intervals.static_function_bodies[output->intervals.static_function_bodies_size];
+  return size;
+}
+
+size_t CodeGenHelper::get_next_chunk_pos(const output_struct * const from, output_struct * const to, const size_t base_pos, const size_t chunk_size) {
+  size_t pos = 0; // Holds the position from the beginning
+  
+  pos += from->intervals.pre_things_size;
+
+  if (pos > base_pos) {
+    to->source.global_vars = mputstr(to->source.global_vars, from->source.global_vars);
+    to->source.string_literals = mputstr(to->source.string_literals, from->source.string_literals);
+  }
+  
+  get_chunk_from_poslist(from->source.methods, to->source.methods, from->intervals.methods, from->intervals.methods_size, base_pos, chunk_size, pos);
+  get_chunk_from_poslist(from->source.function_bodies, to->source.function_bodies, from->intervals.function_bodies, from->intervals.function_bodies_size, base_pos, chunk_size, pos);
+  get_chunk_from_poslist(from->source.static_function_bodies, to->source.static_function_bodies, from->intervals.static_function_bodies, from->intervals.static_function_bodies_size, base_pos, chunk_size, pos);
+  get_chunk_from_poslist(from->source.static_conversion_function_bodies, to->source.static_conversion_function_bodies, from->intervals.static_conversion_function_bodies, from->intervals.static_conversion_function_bodies_size, base_pos, chunk_size, pos);
+
+  return pos;
+}
+//if from null return.
+void CodeGenHelper::get_chunk_from_poslist(const char* from, char *& to, const size_t interval_array[], const size_t interval_array_size, const size_t base_pos, const size_t chunk_size, size_t& pos) {
+  if (from == NULL) return;
+  // If we have enough to form a chunk
+  
+  // pos is unsigned so it can't be negative
+  if (pos > base_pos && pos - base_pos >= chunk_size) return;
+  
+  size_t tmp = pos;
+  
+  pos += interval_array[interval_array_size];
+
+  if (pos > base_pos) { // If we haven't finished with this interval_array
+    if (pos - base_pos >= chunk_size) { // It is a good chunk, but make it more precise because it may be too big
+      int ind = 0;
+      for (int i = 0; i <= interval_array_size; i++) {
+        if (tmp + interval_array[i] <= base_pos) { // Find the pos where we left off
+          ind = i;
+        } else if (tmp + interval_array[i] - base_pos >= chunk_size) {
+          // Found the first optimal position that is a good chunk
+          to = mputstrn(to, from + interval_array[ind], interval_array[i] - interval_array[ind]);
+          pos = tmp + interval_array[i];
+          return;
+        }
+      }
+    } else { // We can't form a new chunk from the remaining characters
+      int ind = 0;
+      for (int i = 0; i <= interval_array_size; i++) {
+        if (tmp + interval_array[i] <= base_pos) {
+          ind = i;
+        } else {
+          break;
+        }
+      }
+      // Put the remaining characters
+      to = mputstrn(to, from + interval_array[ind], interval_array[interval_array_size] - interval_array[ind]);
+      pos = tmp + interval_array[interval_array_size]; 
+    }
+  }
+}
+
 output_struct* CodeGenHelper::get_outputstruct(Ttcn::Definition* def) {
   string key = get_key(*def);
   const string& new_name = current_module + key;
@@ -237,6 +385,8 @@ string CodeGenHelper::get_key(Ttcn::Definition& def) const {
     break;
   case SPLIT_BY_HEURISTICS:
     break;
+  case SPLIT_TO_SLICES:
+    break;
   }
   return retval;
 }
@@ -268,6 +418,8 @@ string CodeGenHelper::get_key(Type& type) const {
     break;
   case SPLIT_BY_HEURISTICS:
     break;
+  case SPLIT_TO_SLICES:
+    break;
   }
   return retval;
 }
@@ -286,12 +438,123 @@ void CodeGenHelper::write_output() {
           go->modulename = modules[j]->name;
           go->module_dispname = modules[j]->dispname;
           go->os.source.includes = mcopystr(
-            "\n//This file intentionally empty."
+            "\n//This file is intentionally empty."
             "\n#include <version.h>\n");
           generated_code.add(fname, go);
         }
       }
     }
+  } else if (split_mode == SPLIT_TO_SLICES && slice_num > 0) {
+    // The strategy is the following:
+    // Goal: Get files with equal size
+    // Get the longest file's length and divide it by slice_num (chunk_size)
+    // We split every file to chunk_size sized chunks
+    size_t max = 0;
+    // Calculate maximum character length
+    for (j = 0; j < generated_code.size(); j++) {
+      update_intervals(&generated_code.get_nth_elem(j)->os);
+      size_t num_of_chars = size_of_sources(&generated_code.get_nth_elem(j)->os);
+      if (max < num_of_chars) {
+        max = num_of_chars;
+      }
+    }
+    // Calculate ideal chunk size
+    size_t chunk_size = max / slice_num;
+    string fname;
+    for (j = 0; j < modules.size(); j++) {
+      generated_output_t *output = generated_code[modules[j]->dispname];
+
+      // Just to be sure that everything is in the right place
+      update_intervals(&output->os);
+      
+      // Move static function prototypes to header (no longer static)
+      output->os.header.function_prototypes = mputstr(output->os.header.function_prototypes, output->os.source.static_function_prototypes);
+      Free(output->os.source.static_function_prototypes);
+      output->os.source.static_function_prototypes = NULL;
+      
+      output->os.header.function_prototypes = mputstr(output->os.header.function_prototypes, output->os.source.static_conversion_function_prototypes);
+      Free(output->os.source.static_conversion_function_prototypes);
+      output->os.source.static_conversion_function_prototypes = NULL;
+      
+      // Move internal class definitions to the header
+      output->os.header.class_defs = mputstr(output->os.header.class_defs, output->os.source.class_defs);
+      Free(output->os.source.class_defs);
+      output->os.source.class_defs = NULL;
+      
+      update_intervals(&output->os);
+      size_t num_of_chars = size_of_sources(&output->os);
+      char buffer[13]=  ""; // Max is 999999 should be enough (checked in main.cc) | 6 digit + 2 underscore + part
+      // If we need to split
+      if (num_of_chars >= chunk_size) {
+        size_t base_pos = 0;
+        for (unsigned int i = 0; i < slice_num; i++) {
+          if (i == 0) { // The first slice has the module's name
+            fname = output->module_dispname;
+          } else {
+            sprintf(buffer, "_part_%d", (int)i);
+            fname = output->module_dispname + "_" + buffer;
+          }
+          if (i == 0 || !generated_code.has_key(fname)) {
+            generated_output_t* go = new generated_output_t;
+            go->filename = buffer;
+            go->modulename = output->modulename;
+            go->module_dispname = output->module_dispname;
+            size_t act_pos = get_next_chunk_pos(&output->os, &go->os, base_pos, chunk_size);
+            // True if the file is not empty
+            if (act_pos > base_pos) {
+              go->os.source.includes = mputstr(go->os.source.includes, output->os.source.includes);
+            } else {
+              go->os.source.includes = mcopystr(
+                "\n//This file is intentionally empty."
+                "\n#include <version.h>\n");
+            }
+            // First slice: copy header and other properties and replace the original output struct
+            if (i == 0) {
+              go->has_circular_import = output->has_circular_import;
+              go->is_module = output->is_module;
+              go->is_ttcn = output->is_ttcn;
+              go->os.header.class_decls = mputstr(go->os.header.class_decls, output->os.header.class_decls);
+              go->os.header.class_defs = mputstr(go->os.header.class_defs, output->os.header.class_defs);
+              go->os.header.function_prototypes = mputstr(go->os.header.function_prototypes, output->os.header.function_prototypes);
+              go->os.header.global_vars = mputstr(go->os.header.global_vars, output->os.header.global_vars);
+              go->os.header.includes = mputstr(go->os.header.includes, output->os.header.includes);
+              go->os.header.testport_includes = mputstr(go->os.header.testport_includes, output->os.header.testport_includes);
+              go->os.header.typedefs = mputstr(go->os.header.typedefs, output->os.header.typedefs);
+              generated_code[modules[j]->dispname] = go;
+            } else {
+              generated_code.add(fname, go);
+            }
+            base_pos = act_pos;
+          } else {
+            // TODO: error handling: there is a module which has the same name as the
+            // numbered splitted file. splitting by type does not have this error
+            // handling so don't we
+          }
+        }
+        // Extra safety. If something is missing after the splitting, put the remaining 
+        // things to the last file. (Should never happen)
+        if (base_pos < num_of_chars) {
+          get_next_chunk_pos(&output->os, &generated_code[fname]->os, base_pos, num_of_chars);
+        }
+        delete output;
+      } else {
+        // Create empty files.
+        for (i = 1; i < slice_num; i++) {
+          sprintf(buffer, "_part_%d", (int)i);
+          fname = output->module_dispname + "_" + buffer;
+          if (!generated_code.has_key(fname)) {
+            generated_output_t* go = new generated_output_t;
+            go->filename = buffer;
+            go->modulename = modules[j]->name;
+            go->module_dispname = modules[j]->dispname;
+            go->os.source.includes = mcopystr(
+              "\n//This file is intentionally empty."
+              "\n#include <version.h>\n");
+            generated_code.add(fname, go);
+          }
+        }
+      }
+    }
   }
   generated_output_t* go;
   for (i = 0; i < generated_code.size(); i++) {
This page took 0.027967 seconds and 5 git commands to generate.