From a7ac4ea600ae29eb0feb6c3779c16987320aa32e Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sat, 22 Mar 2025 22:58:43 +0300 Subject: [PATCH 1/7] Fix application crash due to IllegalStateException --- app/src/main/java/org/qp/android/ui/stock/StockActivity.java | 2 ++ app/src/main/java/org/qp/android/ui/stock/StockViewModel.java | 1 + 2 files changed, 3 insertions(+) diff --git a/app/src/main/java/org/qp/android/ui/stock/StockActivity.java b/app/src/main/java/org/qp/android/ui/stock/StockActivity.java index 058a8f2e..f7bade3c 100644 --- a/app/src/main/java/org/qp/android/ui/stock/StockActivity.java +++ b/app/src/main/java/org/qp/android/ui/stock/StockActivity.java @@ -500,6 +500,8 @@ private void loadSettings() { } public void showErrorDialog(String errorMessage) { + if (isFinishing() || isDestroyed()) return; + stockViewModel.showDialogFragment(getSupportFragmentManager(), StockDialogType.ERROR_DIALOG, errorMessage); } diff --git a/app/src/main/java/org/qp/android/ui/stock/StockViewModel.java b/app/src/main/java/org/qp/android/ui/stock/StockViewModel.java index 5e2500e6..8fc4391a 100644 --- a/app/src/main/java/org/qp/android/ui/stock/StockViewModel.java +++ b/app/src/main/java/org/qp/android/ui/stock/StockViewModel.java @@ -525,6 +525,7 @@ public void showDialogFragment(FragmentManager manager, if (fragment != null && fragment.isAdded()) { fragment.onDestroy(); } else { + if (manager.isDestroyed()) return; switch (dialogType) { case DELETE_DIALOG -> { outputIntObserver = new MutableLiveData<>(); From 75f9bb7c226722c7454cd98df467a5268b94a1b2 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sat, 22 Mar 2025 23:30:03 +0300 Subject: [PATCH 2/7] Refactor RecyclerItemClickListener --- .../adapters/RecyclerItemClickListener.java | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/org/qp/android/helpers/adapters/RecyclerItemClickListener.java b/app/src/main/java/org/qp/android/helpers/adapters/RecyclerItemClickListener.java index bc5d4d1e..bbf32873 100644 --- a/app/src/main/java/org/qp/android/helpers/adapters/RecyclerItemClickListener.java +++ b/app/src/main/java/org/qp/android/helpers/adapters/RecyclerItemClickListener.java @@ -9,20 +9,15 @@ import androidx.recyclerview.widget.RecyclerView; public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener { + private final OnItemClickListener mListener; private final GestureDetector mGestureDetector; - public interface OnItemClickListener { - void onItemClick(View view , int position); - - void onLongItemClick(View view , int position); - } - - public RecyclerItemClickListener(Context context , - final RecyclerView recyclerView , + public RecyclerItemClickListener(Context context, + final RecyclerView recyclerView, OnItemClickListener listener) { mListener = listener; - mGestureDetector = new GestureDetector(context , + mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp(@NonNull MotionEvent e) { @@ -31,30 +26,36 @@ public boolean onSingleTapUp(@NonNull MotionEvent e) { @Override public void onLongPress(@NonNull MotionEvent e) { - var child = recyclerView.findChildViewUnder(e.getX() , e.getY()); + var child = recyclerView.findChildViewUnder(e.getX(), e.getY()); if (child != null && mListener != null) { - mListener.onLongItemClick(child , recyclerView.getChildAdapterPosition(child)); + mListener.onLongItemClick(child, recyclerView.getChildAdapterPosition(child)); } } }); } @Override - public boolean onInterceptTouchEvent(RecyclerView view , MotionEvent e) { - var childView = view.findChildViewUnder(e.getX() , e.getY()); + public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) { + var childView = view.findChildViewUnder(e.getX(), e.getY()); if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) { - mListener.onItemClick(childView , view.getChildAdapterPosition(childView)); + mListener.onItemClick(childView, view.getChildAdapterPosition(childView)); return true; } return false; } @Override - public void onTouchEvent(@NonNull RecyclerView view , + public void onTouchEvent(@NonNull RecyclerView view, @NonNull MotionEvent motionEvent) { } @Override public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { } + + public interface OnItemClickListener { + void onItemClick(View view, int position); + + void onLongItemClick(View view, int position); + } } From c7b7d909f4fc9fef916494537b308687978e5c74 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sat, 22 Mar 2025 23:45:47 +0300 Subject: [PATCH 3/7] Fix error validateViewHolderForOffsetPosition --- .../WrapContentLinearLayoutManager.java | 31 +++++++++++++++++++ .../qp/android/ui/game/GameMainFragment.java | 9 ++++-- .../android/ui/game/GameObjectFragment.java | 10 ++++-- 3 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/org/qp/android/helpers/adapters/WrapContentLinearLayoutManager.java diff --git a/app/src/main/java/org/qp/android/helpers/adapters/WrapContentLinearLayoutManager.java b/app/src/main/java/org/qp/android/helpers/adapters/WrapContentLinearLayoutManager.java new file mode 100644 index 00000000..c9104017 --- /dev/null +++ b/app/src/main/java/org/qp/android/helpers/adapters/WrapContentLinearLayoutManager.java @@ -0,0 +1,31 @@ +package org.qp.android.helpers.adapters; + +import android.content.Context; +import android.util.AttributeSet; + +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +public class WrapContentLinearLayoutManager extends LinearLayoutManager { + + public WrapContentLinearLayoutManager(Context context) { + super(context); + } + + public WrapContentLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public WrapContentLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { + super(context, orientation, reverseLayout); + } + + @Override + public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { + try { + super.onLayoutChildren(recycler, state); + } catch (IndexOutOfBoundsException e) { + // do nothing + } + } +} diff --git a/app/src/main/java/org/qp/android/ui/game/GameMainFragment.java b/app/src/main/java/org/qp/android/ui/game/GameMainFragment.java index 1e301c9e..426a2788 100644 --- a/app/src/main/java/org/qp/android/ui/game/GameMainFragment.java +++ b/app/src/main/java/org/qp/android/ui/game/GameMainFragment.java @@ -21,6 +21,7 @@ import org.qp.android.R; import org.qp.android.databinding.FragmentGameMainBinding; import org.qp.android.helpers.adapters.RecyclerItemClickListener; +import org.qp.android.helpers.adapters.WrapContentLinearLayoutManager; public class GameMainFragment extends Fragment { @@ -88,10 +89,14 @@ public void onClickImage(String src) { null)); // RecyclerView + var layoutManager = new WrapContentLinearLayoutManager( + requireContext(), LinearLayoutManager.VERTICAL, false + ); actionsView = gameMainBinding.actions; - var manager = (LinearLayoutManager) actionsView.getLayoutManager(); + actionsView.setLayoutManager(layoutManager); var dividerItemDecoration = new DividerItemDecoration( - actionsView.getContext(), manager.getOrientation()); + actionsView.getContext(), layoutManager.getOrientation() + ); actionsView.addItemDecoration(dividerItemDecoration); actionsView.setOverScrollMode(View.OVER_SCROLL_NEVER); actionsView.setBackgroundColor(viewModel.getBackgroundColor()); diff --git a/app/src/main/java/org/qp/android/ui/game/GameObjectFragment.java b/app/src/main/java/org/qp/android/ui/game/GameObjectFragment.java index 25f0e1b6..4c7ec8fb 100644 --- a/app/src/main/java/org/qp/android/ui/game/GameObjectFragment.java +++ b/app/src/main/java/org/qp/android/ui/game/GameObjectFragment.java @@ -15,6 +15,7 @@ import org.qp.android.databinding.FragmentRecyclerBinding; import org.qp.android.helpers.adapters.RecyclerItemClickListener; +import org.qp.android.helpers.adapters.WrapContentLinearLayoutManager; public class GameObjectFragment extends Fragment { @@ -32,11 +33,14 @@ public View onCreateView(@NonNull LayoutInflater inflater, viewModel = new ViewModelProvider(requireActivity()).get(GameViewModel.class); // RecyclerView + var layoutManager = new WrapContentLinearLayoutManager( + requireContext(), LinearLayoutManager.VERTICAL, false + ); objectView = recyclerBinding.shareRecyclerView; - var manager = (LinearLayoutManager) objectView.getLayoutManager(); + objectView.setLayoutManager(layoutManager); var dividerItemDecoration = new DividerItemDecoration( - objectView.getContext(), - manager.getOrientation()); + objectView.getContext(), layoutManager.getOrientation() + ); objectView.addItemDecoration(dividerItemDecoration); objectView.setOverScrollMode(View.OVER_SCROLL_NEVER); objectView.setBackgroundColor(viewModel.getBackgroundColor()); From 36787ca02d7243abfcf43c20fd502efd6e5a48d6 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sat, 22 Mar 2025 23:57:02 +0300 Subject: [PATCH 4/7] Reduce the use of DataBinding --- .../qp/android/ui/game/GameMainFragment.java | 25 ++++-- .../org/qp/android/ui/game/GameViewModel.java | 6 +- .../main/res/layout/fragment_game_main.xml | 85 ++++++++----------- 3 files changed, 54 insertions(+), 62 deletions(-) diff --git a/app/src/main/java/org/qp/android/ui/game/GameMainFragment.java b/app/src/main/java/org/qp/android/ui/game/GameMainFragment.java index 426a2788..356c1a99 100644 --- a/app/src/main/java/org/qp/android/ui/game/GameMainFragment.java +++ b/app/src/main/java/org/qp/android/ui/game/GameMainFragment.java @@ -1,5 +1,8 @@ package org.qp.android.ui.game; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + import android.net.Uri; import android.os.Bundle; import android.view.LayoutInflater; @@ -47,7 +50,6 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable Bundle savedInstanceState) { var gameMainBinding = FragmentGameMainBinding.inflate(getLayoutInflater()); viewModel = new ViewModelProvider(requireActivity()).get(GameViewModel.class); - gameMainBinding.setGameViewModel(viewModel); layoutTop = gameMainBinding.layoutTop; layoutTop.setBackgroundColor(viewModel.getBackgroundColor()); @@ -103,13 +105,20 @@ public void onClickImage(String src) { actionsView.setAdapter(adapter); viewModel.actsListLiveData.observe(getViewLifecycleOwner(), listItems -> { - actionsView.setBackgroundColor(viewModel.getBackgroundColor()); - adapter.typeface = viewModel.getSettingsController().getTypeface(); - adapter.textSize = viewModel.getFontSize(); - adapter.textColor = viewModel.getTextColor(); - adapter.linkTextColor = viewModel.getLinkColor(); - adapter.backgroundColor = viewModel.getBackgroundColor(); - adapter.submitList(listItems); + if (!viewModel.showActions || listItems.isEmpty()) { + actionsView.setVisibility(GONE); + separatorView.setVisibility(GONE); + } else { + actionsView.setVisibility(VISIBLE); + separatorView.setVisibility(VISIBLE); + actionsView.setBackgroundColor(viewModel.getBackgroundColor()); + adapter.typeface = viewModel.getSettingsController().getTypeface(); + adapter.textSize = viewModel.getFontSize(); + adapter.textColor = viewModel.getTextColor(); + adapter.linkTextColor = viewModel.getLinkColor(); + adapter.backgroundColor = viewModel.getBackgroundColor(); + adapter.submitList(listItems); + } }); // Settings diff --git a/app/src/main/java/org/qp/android/ui/game/GameViewModel.java b/app/src/main/java/org/qp/android/ui/game/GameViewModel.java index a6d9d7ce..852c71d6 100644 --- a/app/src/main/java/org/qp/android/ui/game/GameViewModel.java +++ b/app/src/main/java/org/qp/android/ui/game/GameViewModel.java @@ -30,7 +30,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.databinding.ObservableBoolean; import androidx.documentfile.provider.DocumentFile; import androidx.fragment.app.DialogFragment; import androidx.lifecycle.AndroidViewModel; @@ -91,7 +90,6 @@ public class GameViewModel extends AndroidViewModel implements GameInterface { public final MutableLiveData> actsListLiveData = new MutableLiveData<>(); public final MutableLiveData> objsListLiveData = new MutableLiveData<>(); private final Handler counterHandler = new Handler(); - public ObservableBoolean isActionVisible = new ObservableBoolean(); public MutableLiveData outputTextObserver = new MutableLiveData<>(); public MutableLiveData outputIntObserver = new MutableLiveData<>(); public MutableLiveData outputBooleanObserver = new MutableLiveData<>(false); @@ -99,7 +97,7 @@ public class GameViewModel extends AndroidViewModel implements GameInterface { public String pageTemplate = ""; public SharedPreferences preferences; private Uri gameDirUri; - private boolean showActions = true; + public boolean showActions = true; SharedPreferences.OnSharedPreferenceChangeListener preferenceChangeListener = (sharedPreferences, key) -> { controllerObserver.postValue(getSettingsController()); updatePageTemplate(); @@ -391,8 +389,6 @@ public void onActionClicked(int index) { private void refreshActionsRecycler() { var listItems = getLibGameState().actionsList; - var listSize = listItems.size(); - isActionVisible.set(showActions && listSize > 0); actsListLiveData.postValue(listItems); } diff --git a/app/src/main/res/layout/fragment_game_main.xml b/app/src/main/res/layout/fragment_game_main.xml index 92654684..ad6f4533 100644 --- a/app/src/main/res/layout/fragment_game_main.xml +++ b/app/src/main/res/layout/fragment_game_main.xml @@ -1,55 +1,42 @@ - - - + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/layout_top" + android:layout_width="match_parent" + android:layout_height="match_parent"> - - + - + - + - - - - - - \ No newline at end of file + \ No newline at end of file From 83e0ce028931f0abc8b349932afa99839876e982 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Sun, 23 Mar 2025 00:07:35 +0300 Subject: [PATCH 5/7] Fix NullPointerException error --- .../java/org/qp/android/ui/stock/StockViewModel.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/qp/android/ui/stock/StockViewModel.java b/app/src/main/java/org/qp/android/ui/stock/StockViewModel.java index 8fc4391a..405ec399 100644 --- a/app/src/main/java/org/qp/android/ui/stock/StockViewModel.java +++ b/app/src/main/java/org/qp/android/ui/stock/StockViewModel.java @@ -927,13 +927,16 @@ private void dropPersistable(Uri folderUri) { } public void delEntryDirFromList(List tempList, GameData data, File listDirsFile) { + if (data.gameDirUri == null) return; var gameDir = DocumentFileCompat.fromUri(getApplication(), data.gameDirUri); if (!isWritableDir(getApplication(), gameDir)) return; + var nameGameDir = gameDir.getName(); + if (!isNotEmptyOrBlank(nameGameDir)) return; CompletableFuture .runAsync(() -> tempList.remove(data), executor) .thenCombineAsync( - removeDirFromListDirsFile(listDirsFile, gameDir.getName()), + removeDirFromListDirsFile(listDirsFile, nameGameDir), (unused, unused2) -> null, executor ) @@ -947,13 +950,16 @@ public void delEntryDirFromList(List tempList, GameData data, File lis } public void delEntryFromList(List tempList, GameData data, File listDirsFile) { + if (data.gameDirUri == null) return; var gameDir = DocumentFileCompat.fromUri(getApplication(), data.gameDirUri); if (!isWritableDir(getApplication(), gameDir)) return; + var nameGameDir = gameDir.getName(); + if (!isNotEmptyOrBlank(nameGameDir)) return; CompletableFuture .runAsync(() -> tempList.remove(data), executor) .thenCombineAsync( - removeDirFromListDirsFile(listDirsFile, gameDir.getName()), + removeDirFromListDirsFile(listDirsFile, nameGameDir), (unused, unused2) -> null, executor ) From 41122ee37328ac206f5a9af4c0115051e4d21cdd Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Thu, 3 Apr 2025 23:18:05 +0300 Subject: [PATCH 6/7] Update the native library --- app/src/main/cpp/CMakeLists.txt | 78 ++++++++++++++++---------------- app/src/main/cpp/qsp/game.c | 6 +-- app/src/main/cpp/qsp/variables.c | 20 ++++---- app/src/main/cpp/qsp/variables.h | 3 +- 4 files changed, 54 insertions(+), 53 deletions(-) diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 3c8d7df1..39cf947f 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -77,7 +77,7 @@ else() set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) add_subdirectory("${CMAKE_BINARY_DIR}/oniguruma-src" - "${CMAKE_BINARY_DIR}/oniguruma-build" + "${CMAKE_BINARY_DIR}/oniguruma-build" ) add_library(oniguruma::onig ALIAS onig) endif() @@ -85,36 +85,36 @@ endif() configure_file(qsp_config.h.cmakein qsp_config.h @ONLY) set(QSP_SOURCES - qsp/bindings/bindings_config.h - qsp/bindings/default/default_callbacks.c - qsp/bindings/default/default_control.c - qsp/bindings/default/qsp_default.h - qsp/bindings/java/java_callbacks.c - qsp/bindings/java/java_control.c - qsp/bindings/java/qsp_java.h - qsp/bindings/qsp.h - qsp/actions.c qsp/actions.h - qsp/callbacks.c qsp/callbacks.h - qsp/codetools.c qsp/codetools.h - qsp/coding.c qsp/coding.h - qsp/common.c qsp/common.h - qsp/errors.c qsp/errors.h - qsp/game.c qsp/game.h - qsp/locations.c qsp/locations.h - qsp/mathops.c qsp/mathops.h - qsp/memwatch.c qsp/memwatch.h - qsp/menu.c qsp/menu.h - qsp/objects.c qsp/objects.h - qsp/playlist.c qsp/playlist.h - qsp/regexp.c qsp/regexp.h - qsp/statements.c qsp/statements.h - qsp/text.c qsp/text.h - qsp/time.c qsp/time.h - qsp/towlower.c - qsp/towupper.c - qsp/tuples.c qsp/tuples.h - qsp/variables.c qsp/variables.h - qsp/variant.c qsp/variant.h + qsp/bindings/bindings_config.h + qsp/bindings/default/default_callbacks.c + qsp/bindings/default/default_control.c + qsp/bindings/default/qsp_default.h + qsp/bindings/java/java_callbacks.c + qsp/bindings/java/java_control.c + qsp/bindings/java/qsp_java.h + qsp/bindings/qsp.h + qsp/actions.c qsp/actions.h + qsp/callbacks.c qsp/callbacks.h + qsp/codetools.c qsp/codetools.h + qsp/coding.c qsp/coding.h + qsp/common.c qsp/common.h + qsp/errors.c qsp/errors.h + qsp/game.c qsp/game.h + qsp/locations.c qsp/locations.h + qsp/mathops.c qsp/mathops.h + qsp/memwatch.c qsp/memwatch.h + qsp/menu.c qsp/menu.h + qsp/objects.c qsp/objects.h + qsp/playlist.c qsp/playlist.h + qsp/regexp.c qsp/regexp.h + qsp/statements.c qsp/statements.h + qsp/text.c qsp/text.h + qsp/time.c qsp/time.h + qsp/towlower.c + qsp/towupper.c + qsp/tuples.c qsp/tuples.h + qsp/variables.c qsp/variables.h + qsp/variant.c qsp/variant.h ) add_library(qsp SHARED ${QSP_SOURCES}) target_compile_definitions(qsp PUBLIC _UNICODE) @@ -135,7 +135,7 @@ if (BUILD_JAVA) endif() target_include_directories(qsp - INTERFACE + INTERFACE "$" "$" "$" @@ -145,33 +145,33 @@ target_include_directories(qsp install(TARGETS qsp EXPORT QspTargets) generate_export_header(qsp - BASE_NAME Qsp - EXPORT_MACRO_NAME QSP_EXTERN + BASE_NAME Qsp + EXPORT_MACRO_NAME QSP_EXTERN ) write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/cmake/Qsp/QspConfigVersion.cmake COMPATIBILITY AnyNewerVersion) configure_package_config_file(QspConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/cmake/Qsp/QspConfig.cmake - INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Qsp" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Qsp" ) export(TARGETS qsp NAMESPACE Qsp:: FILE ${CMAKE_CURRENT_BINARY_DIR}/QspConfig.cmake) install(EXPORT QspTargets NAMESPACE Qsp:: DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Qsp") install( - DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/cmake - DESTINATION "${CMAKE_INSTALL_LIBDIR}" + DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/cmake + DESTINATION "${CMAKE_INSTALL_LIBDIR}" ) install(FILES qsp/bindings/qsp.h qsp/bindings/bindings_config.h "${CMAKE_CURRENT_BINARY_DIR}/qsp_export.h" - DESTINATION + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/qsp" ) install(FILES qsp/bindings/default/qsp_default.h - DESTINATION + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/qsp/default" ) diff --git a/app/src/main/cpp/qsp/game.c b/app/src/main/cpp/qsp/game.c index d6bbd46b..102f80ff 100644 --- a/app/src/main/cpp/qsp/game.c +++ b/app/src/main/cpp/qsp/game.c @@ -483,7 +483,7 @@ INLINE QSP_BOOL qspCheckGameStatus(QSPString *strs, int strsCount, QSP_BOOL isUC { /* variables count */ if (!qspGetIntValueAndSkipLine(strs, strsCount, &ind, isUCS, &count)) return QSP_FALSE; - if (count < 0 || count > QSP_VARSMAXBUCKETSIZE) return QSP_FALSE; + if (count < 0 || count > QSP_VARSBUCKETSIZE) return QSP_FALSE; /* variables */ for (j = 0; j < count; ++j) { @@ -586,11 +586,11 @@ QSP_BOOL qspOpenGameStatus(void *data, int dataSize) for (i = 0; i < QSP_VARSBUCKETS; ++i) { varsCount = qspReadEncodedIntVal(strs[ind++], isUCS); - bucket->Capacity = bucket->VarsCount = varsCount; + bucket->VarsCount = varsCount; bucket->Vars = 0; if (varsCount) { - var = bucket->Vars = (QSPVar *)malloc(varsCount * sizeof(QSPVar)); + var = bucket->Vars = (QSPVar *)malloc(QSP_VARSBUCKETSIZE * sizeof(QSPVar)); for (j = 0; j < varsCount; ++j) { var->Name = qspDecodeString(strs[ind++], isUCS); diff --git a/app/src/main/cpp/qsp/variables.c b/app/src/main/cpp/qsp/variables.c index 79ca1b72..d2fe0e7f 100644 --- a/app/src/main/cpp/qsp/variables.c +++ b/app/src/main/cpp/qsp/variables.c @@ -127,19 +127,21 @@ QSPVar *qspVarReference(QSPString name, QSP_BOOL toCreate) } if (toCreate) { - if (varsCount >= QSP_VARSMAXBUCKETSIZE) + if (bucket->Vars) { - qspSetError(QSP_ERR_TOOMANYVARS); - return 0; + if (varsCount >= QSP_VARSBUCKETSIZE) + { + qspSetError(QSP_ERR_TOOMANYVARS); + return 0; + } + var = bucket->Vars + varsCount; } - if (varsCount >= bucket->Capacity) + else { - bucket->Capacity = varsCount + 16; - bucket->Vars = (QSPVar *)realloc(bucket->Vars, bucket->Capacity * sizeof(QSPVar)); + /* The fixed buffer without memory reallocation helps to cache specific variables */ + var = bucket->Vars = (QSPVar *)malloc(QSP_VARSBUCKETSIZE * sizeof(QSPVar)); } - var = bucket->Vars + varsCount; - var->Name = qspCopyToNewText(name); qspInitVarData(var); @@ -167,7 +169,7 @@ void qspClearAllVars(QSP_BOOL toInit) } free(bucket->Vars); } - bucket->Capacity = bucket->VarsCount = 0; + bucket->VarsCount = 0; bucket->Vars = 0; ++bucket; } diff --git a/app/src/main/cpp/qsp/variables.h b/app/src/main/cpp/qsp/variables.h index 05f23db8..9726a959 100644 --- a/app/src/main/cpp/qsp/variables.h +++ b/app/src/main/cpp/qsp/variables.h @@ -24,7 +24,7 @@ #define QSP_VARSDEFINES #define QSP_VARSBUCKETS 1024 - #define QSP_VARSMAXBUCKETSIZE 50 + #define QSP_VARSBUCKETSIZE 32 #define QSP_VARGROUPSBATCHSIZE 256 #define QSP_VARARGS QSP_FMT("ARGS") #define QSP_VARRES QSP_FMT("RESULT") @@ -50,7 +50,6 @@ { QSPVar *Vars; int VarsCount; - int Capacity; } QSPVarsBucket; typedef struct From a691c12638f92670e09566b579227761db6df5c4 Mon Sep 17 00:00:00 2001 From: WhoYouAndM3 <83360444+l3ger0j@users.noreply.github.com> Date: Tue, 15 Apr 2025 02:06:27 +0300 Subject: [PATCH 7/7] Bump to 3.25.5 --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index a6ae4339..7337d143 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,8 +10,8 @@ android { applicationId "org.qp.android" minSdk 26 targetSdk 34 - versionCode 202504 - versionName "3.25.4" + versionCode 202505 + versionName "3.25.5" resourceConfigurations += ['en', 'ru'] testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" externalNativeBuild {