Ka Nang Leung

95 papers C 12Journal 58Unranked 25
YearRankTypeTitle / Venue / Authors
2026 conf
ISSCC
Rui Zhang, Chuhui Wang, Zhongxin Ye, Ka Nang Leung, Jianping Guo
2026 J jnl
IEEE Trans. Circuits Syst. I Regul. Pap.
Yinglyu Xiong, Xun Liu, Ka Nang Leung
2026 conf
ASP-DAC
Junyi Ruan, Junmin Jiang, Chenzhou Ding, Yanhui Wu, Ka Nang Leung, Xun Liu
2026 J jnl
Microelectron. J.
Meng Wang, Changhong Peng, Ka Nang Leung, Yanqi Zheng
2025 J jnl
IEEE Solid State Circuits Lett.
Hing Tai Chen, Xun Liu, Ka Nang Leung
2025 J jnl
IEEE Trans. Circuits Syst. I Regul. Pap.
Yun-Hung Gao, Junwen Li, Chun-Ho Fan, Ka Nang Leung, Yuan-Ting Zhang, Kong-Pang Pun
2025 J jnl
IEEE J. Solid State Circuits
Chuhui Wang, Jianping Guo, Ka Nang Leung
2025 J jnl
IEEE J. Solid State Circuits
Yajun Lin, Hing Tai Chen, Xun Liu, Ka Nang Leung
2025 J jnl
IEEE J. Solid State Circuits
Junyi Ruan, Junmin Jiang, Chenzhou Ding, Kai Yuan, Ka Nang Leung, Zhiyuan Chen, Xiaoyang Zeng, Xun Liu
2025 C conf
ISCAS
Yinglyu Xiong, Zehao Tao, Miaomiao Chen, Xun Liu, Ka Nang Leung
2025 J jnl
IEEE Trans. Ind. Electron.
Chao Gao, Shan He, Rui Kong, Ka Nang Leung, Poh Chiang Loh
2024 J jnl
IEEE J. Solid State Circuits
Junyi Ruan, Junmin Jiang, Chenzhou Ding, Yunxiao Li, Yanhui Wu, Ka Nang Leung, Xun Liu
2024 conf
A-SSCC
Kai Yuan, Chenzhou Ding, Junmin Jiang, Tiantian Tang, Junyi Ruan, Yelei Xie, Faen Liu, Shuguo Jiang, Tao Liu, Ka Nang Leung, Xun Liu
2024 conf
ISSCC
Junyi Ruan, Junmin Jiang, Chenzhou Ding, Kai Yuan, Ka Nang Leung, Xun Liu
2024 J jnl
IEEE Trans. Very Large Scale Integr. Syst.
Yichen Zhang, Chaowei Tang, Yanqi Zheng, Xian Tang, Ka Nang Leung
2023 C conf
IECON
Chao Gao, Shan He, Pooya Davari, Frede Blaabjerg, Ka Nang Leung, Poh Chiang Loh
2022 J jnl
IEEE Access
Yuet Ho Woo, Jianxin Yang, Jianping Guo, Yanqi Zheng, Ka Nang Leung
2022 J jnl
IEEE Trans. Circuits Syst. II Express Briefs
Jianxin Yang, Bin Li, Ka Nang Leung, Zhijian Chen, Yanqi Zheng
2022 J jnl
IEEE Access
Yuet Ho Woo, Jianxin Yang, Junwen Li, Jianping Guo, Yanqi Zheng, Ka Nang Leung
2022 conf
APCCAS
Yajun Lin, Haozheng Wan, Jianxin Yang, Ka Nang Leung
2022 J jnl
IEEE Trans. Circuits Syst. II Express Briefs
Yong Zhou, Qingqing Cheng, Junwen Li, Yanqi Zheng, Ka Nang Leung
2021 J jnl
Int. J. Circuit Theory Appl.
Tin Wai Mui, Jianxin Yang, Yajun Lin, Yong Zhou, Yanqi Zheng, Jianping Guo, Ka Nang Leung
2020 J jnl
IEEE Access
Xiangbin Ding, Pak Kwong Chan, Ka Nang Leung
2020 J jnl
Int. J. Circuit Theory Appl.
Yong Zhou, Xiaohui Lin, Jianxin Yang, Yanqi Zheng, Ka Nang Leung
2020 C conf
ISCAS
Yong Zhou, Yanqi Zheng, Ka Nang Leung
2020 J jnl
IEEE Trans. Circuits Syst. II Express Briefs
Cheuk Ho Hung, Yanqi Zheng, Jianping Guo, Ka Nang Leung
2019 J jnl
Int. J. Circuit Theory Appl.
Yuet Ho Woo, Ka Nang Leung, Yanqi Zheng, Jianping Guo
2018 J jnl
Microelectron. J.
Zhao Wang, Biao Chen, Lei Zhu, Yanqi Zheng, Jianping Guo, Dihu Chen, Marco Ho, Ka Nang Leung
2018 J jnl
IEEE Trans. Circuits Syst. I Regul. Pap.
Shi Bu, Ka Nang Leung, Yan Lu, Jianping Guo, Yanqi Zheng
2017 J jnl
Int. J. Circuit Theory Appl.
Ming Wai Lau, Kai Ho Mak, Ka Nang Leung, Jianping Guo, Wang Ling Goh
2016 conf
ISIC
Biao Chen, Lei Zhu, Yanqi Zheng, Jianping Guo, Marco Ho, Ka Nang Leung, Dihu Chen, Yang Liu
2016 J jnl
IEEE Trans. Very Large Scale Integr. Syst.
Marco Ho, Jianping Guo, Tin Wai Mui, Kai Ho Mak, Wang Ling Goh, Hiu Ching Poon, Shi Bu, Ming Wai Lau, Ka Nang Leung
2016 conf
ISIC
Lei Zhu, Biao Chen, Yanqi Zheng, Jianping Guo, Marco Ho, Ka Nang Leung, Dihu Chen, Yang Liu
2016 conf
ISIC
Jingbin Jia, Marco Ho, Ka Nang Leung, Jianping Guo
2016 J jnl
Int. J. Circuit Theory Appl.
Jianping Guo, Marco Ho, Ka Nang Leung, Guangxiang Li
2015 J jnl
IEEE J. Solid State Circuits
Kai Ho Mak, Ming Wai Lau, Jianping Guo, Tin Wai Mui, Marco Ho, Wang Ling Goh, Ka Nang Leung
2015 J jnl
IEEE Trans. Very Large Scale Integr. Syst.
Hao Wang, Xian Tang, Chiu-sing Choy, Ka Nang Leung, Kong-Pang Pun
2015 conf
SoCC
Yanqi Zheng, Marco Ho, Ka Nang Leung, Jianping Guo, Biao Chen
2015 C conf
ISCAS
Yanqi Zheng, Marco Ho, Ka Nang Leung, Jianping Guo
2015 conf
MWSCAS
Yanqi Zheng, Marco Ho, Ka Nang Leung, Jianping Guo, Hua Chen
2015 C conf
ISCAS
Kai Ho Mak, Marco Ho, Ka Nang Leung, Wang Ling Goh
2015 C conf
ISCAS
Shi Bu, Hing Wa Tse, Ka Nang Leung, Jianping Guo, Marco Ho
2014 conf
APCCAS
Marco Ho, Shi Bu, Yanqi Zheng, Ka Nang Leung, Jianping Guo
2014 J jnl
IEEE Trans. Circuits Syst. I Regul. Pap.
Kai Ho Mak, Ka Nang Leung
2014 J jnl
IEEE Trans. Circuits Syst. II Express Briefs
Tin Wai Mui, Marco Ho, Kai Ho Mak, Jianping Guo, Hua Chen, Ka Nang Leung
2014 conf
APCCAS
Marco Ho, Ka Nang Leung, Pui Ying Or, Jianping Guo
2014 conf
APCCAS
Shi Bu, Marco Ho, Ka Nang Leung, Jianping Guo
2013 J jnl
Int. J. Circuit Theory Appl.
Hua Chen, Ka Nang Leung
2013 J jnl
IEEE Trans. Very Large Scale Integr. Syst.
Kwan Wai Li, Ka Nang Leung, Lincoln Lai Kan Leung
2012 J jnl
Int. J. Circuit Theory Appl.
Jianping Guo, Ka Nang Leung
2012 J jnl
IEEE Trans. Very Large Scale Integr. Syst.
Yanqi Zheng, Hua Chen, Ka Nang Leung
2012 conf
APCCAS
Kwan Wai Li, Ka Nang Leung
2012 J jnl
Int. J. Circuit Theory Appl.
Jingbin Jia, Ka Nang Leung
2011 C conf
ISCAS
Ka Nang Leung, Marco Ho, Jianping Guo, Pui Ying Or
2011 J jnl
IEEE Trans. Circuits Syst. II Express Briefs
Marco Ho, Ka Nang Leung
2010 J jnl
IEEE Trans. Circuits Syst. II Express Briefs
Alex K. Y. Wong, Ka Nang Leung, Kong-Pang Pun, Yuan-Ting Zhang
2010 J jnl
J. Low Power Electron.
Ka Nang Leung, Felix Kok Man Cheung, Marco Ho, Hiu Ching Poon, Pui Ying Or
2010 J jnl
IEEE J. Solid State Circuits
Jianping Guo, Ka Nang Leung
2010 J jnl
IEEE Trans. Circuits Syst. I Regul. Pap.
Ka Nang Leung, Yuen Sum Ng
2010 J jnl
IEEE Trans. Very Large Scale Integr. Syst.
Ka Nang Leung, Yuan Yen Mai, Philip K. T. Mok
2010 J jnl
IEEE Trans. Circuits Syst. II Express Briefs
Pui Ying Or, Ka Nang Leung
2010 J jnl
IEEE J. Solid State Circuits
Chi Fat Chan, Kong-Pang Pun, Ka Nang Leung, Jianping Guo, Lincoln Lai Kan Leung, Oliver Chiu-sing Choy
2010 J jnl
IEEE J. Solid State Circuits
Marco Ho, Ka Nang Leung, Ki-Leung Mak
2010 J jnl
IEEE J. Solid State Circuits
Pui Ying Or, Ka Nang Leung
2010 J jnl
J. Low Power Electron.
Ka Nang Leung, Chiu-sing Choy, Kong-Pang Pun, Lincoln Lai Kan Leung, Jianping Guo, Yuen Sum Ng, Chi Fat Chan, Weiwei Shi, Yang Hong, Marco Ho, Ki-Leung Mak, Yanqing Ai
2009 C conf
ISCAS
Jianping Guo, Ka Nang Leung
2009 C conf
ISCAS
Chi Fat Chan, Weiwei Shi, Kong-Pang Pun, Lincoln Lai Kan Leung, Ka Nang Leung, Oliver Chiu-sing Choy
2009 C conf
ISCAS
Jingbin Jia, Ka Nang Leung
2009 C conf
ISCAS
Wen Fan, Oliver Chiu-sing Choy, Ka Nang Leung
2008 J jnl
IEEE Trans. Circuits Syst. II Express Briefs
Siu-Kei Tang, Kong-Pang Pun, Oliver Chiu-sing Choy, Cheong-Fat Chan, Ka Nang Leung
2008 J jnl
IEEE Trans. Biomed. Circuits Syst.
Alex K. Y. Wong, Kong-Pang Pun, Yuan-Ting Zhang, Ka Nang Leung
2008 conf
DELTA
Ka Nang Leung, Yanqi Zheng
2008 conf
DELTA
Chun Yu Cheng, Ka Nang Leung, Yi Ki Sun, Pui Ying Or
2008 conf
APCCAS
Yang Hong, Chi Fat Chan, Jianping Guo, Yuen Sum Ng, Weiwei Shi, Lincoln Lai Kan Leung, Ka Nang Leung, Chiu-sing Choy, Kong-Pang Pun
2008 J jnl
IEEE Trans. Circuits Syst. I Regul. Pap.
Tsz Yin Man, Ka Nang Leung, Chi Yat Leung, Philip K. T. Mok, Mansun Chan
2007 J jnl
IEEE J. Solid State Circuits
Sai Kit Lau, Philip K. T. Mok, Ka Nang Leung
2006 J jnl
IEEE Trans. Circuits Syst. II Express Briefs
Man Siu, Philip K. T. Mok, Ka Nang Leung, Yat-Hei Lam, Wing-Hung Ki
2005 J jnl
IEEE J. Solid State Circuits
Chi Yat Leung, Philip K. T. Mok, Ka Nang Leung
2005 J jnl
IEEE Trans. Circuits Syst. II Express Briefs
Chi Yat Leung, Philip K. T. Mok, Ka Nang Leung, Mansun Chan
2005 J jnl
IEEE Trans. Circuits Syst. II Express Briefs
Hoi Lee, Philip K. T. Mok, Ka Nang Leung
2004 conf
ISCAS (5)
Chi Yat Leung, Philip K. T. Mok, Ka Nang Leung
2004 conf
ISCAS (1)
Ka Nang Leung, Philip K. T. Mok, Sai Kit Lau
2004 conf
CICC
Philip K. T. Mok, Ka Nang Leung
2004 conf
ISCAS (1)
Chi Yat Leung, Ka Nang Leung, Philip K. T. Mok
2003 J jnl
IEEE J. Solid State Circuits
Ka Nang Leung, Philip K. T. Mok, Chi Yat Leung
2003 J jnl
IEEE J. Solid State Circuits
Ka Nang Leung, Philip K. T. Mok
2003 J jnl
IEEE J. Solid State Circuits
Ka Nang Leung, Philip K. T. Mok
2003 J jnl
IEEE J. Solid State Circuits
Hoi Lee, Ka Nang Leung, Philip K. T. Mok
2002 conf
CICC
Ka Nang Leung, Philip K. T. Mok, Chi Yat Leung
2002 J jnl
IEEE J. Solid State Circuits
Ka Nang Leung, Philip K. T. Mok
2000 C conf
ISCAS
Ka Nang Leung, Philip K. T. Mok, Wing-Hung Ki, Johnny K. O. Sin
2000 J jnl
IEEE J. Solid State Circuits
Ka Nang Leung, Philip K. T. Mok, Wing-Hung Ki, Johnny K. O. Sin
1999 conf
ISCAS (5)
Ka Nang Leung, Philip K. T. Mok, Wing-Hung Ki
1999 conf
ISCAS (2)
Ka Nang Leung, Philip K. T. Mok, Wing-Hung Ki
1999 conf
ICECS
Ka Nang Leung, Philip K. T. Mok, Wing-Hung Ki
tests/unit/test_decompile_binja_extractor.py
← Index tests/unit/test_decompile_binja_extractor.py python
"""Unit tests for DecompileBinja (top-level extractor) and prepare_export_data."""
import hashlib
import json
import os
import sys
import pytest
from unittest.mock import MagicMock, patch, PropertyMock
from datetime import datetime

# Install binaryninja stubs
from tests.unit.conftest_binja_stubs import install_binja_stubs
install_binja_stubs()

from redb.extractors.enum import Tag


# ============================================================================
# 7a. DecompileBinja — methods that don't require a live Binary Ninja session
# ============================================================================


class TestDecompileBinjaCalculateMD5:
    def test_calculate_md5(self):
        """calculate_md5("test") returns expected MD5."""
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = object.__new__(DecompileBinja)
            obj.log = MagicMock()
            result = obj.calculate_md5("test")
            expected = hashlib.md5(b"test").hexdigest()
            assert result == expected


class TestDecompileBinjaTimeouts:
    def _make_extractor(self, env_vars=None):
        """Create a DecompileBinja with mocked Extractor.__init__."""
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            if env_vars:
                with patch.dict(os.environ, env_vars, clear=False):
                    obj = DecompileBinja.__new__(DecompileBinja)
                    obj.log = MagicMock()
                    obj.analysis_results = None
                    obj.binja_decompiler = None
                    obj.filetype = None
                    obj.goresym_data = None
                    obj.goresym_output_path = None
                    # Re-run timeout logic
                    try:
                        obj.BINJA_TIMEOUT = int(os.getenv("BINJA_TIMEOUT", "1200"))
                    except ValueError:
                        obj.BINJA_TIMEOUT = 1200
                    try:
                        obj.DECOMPILE_EXTRACTOR_TIMEOUT = int(
                            os.getenv("DECOMPILE_EXTRACTOR_TIMEOUT", "2580")
                        )
                    except ValueError:
                        obj.DECOMPILE_EXTRACTOR_TIMEOUT = 2580
                    return obj
            else:
                obj = DecompileBinja.__new__(DecompileBinja)
                obj.log = MagicMock()
                obj.analysis_results = None
                obj.binja_decompiler = None
                obj.filetype = None
                obj.goresym_data = None
                obj.goresym_output_path = None
                obj.BINJA_TIMEOUT = 1200
                obj.DECOMPILE_EXTRACTOR_TIMEOUT = 2580
                return obj

    def test_timeout_defaults(self):
        obj = self._make_extractor()
        assert obj.BINJA_TIMEOUT == 1200
        assert obj.DECOMPILE_EXTRACTOR_TIMEOUT == 2580

    def test_timeout_from_env(self):
        obj = self._make_extractor(env_vars={"BINJA_TIMEOUT": "600"})
        assert obj.BINJA_TIMEOUT == 600

    def test_timeout_invalid_env(self):
        obj = self._make_extractor(env_vars={"BINJA_TIMEOUT": "not_a_number"})
        assert obj.BINJA_TIMEOUT == 1200


class TestDecompileBinjaDotnet:
    def _make_extractor(self, filetype=None):
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.filetype = filetype
            obj.filepath = "/tmp/fake.bin"
            obj.binary = b"\x00" * 100
            obj.hash = MagicMock()
            obj.hash.sha256 = "a" * 64
            return obj

    def test_is_dotnet_non_pe(self):
        obj = self._make_extractor(filetype="elf")
        assert obj.is_dotnet() is False

    def test_is_dotnet_pe_without_com(self):
        """PE without .NET COM descriptor -> False."""
        obj = self._make_extractor(filetype="pebin")
        # Mock magic to not have .Net
        with patch("redb.extractors.decompiler.DecompileBinja.magic") as mock_magic:
            mock_magic.from_buffer.return_value = "PE32 executable"
            with patch("redb.extractors.decompiler.DecompileBinja.pefile") as mock_pefile:
                mock_pe = MagicMock()
                entry = MagicMock()
                entry.name = "IMAGE_DIRECTORY_ENTRY_IMPORT"
                entry.Size = 100
                mock_pe.OPTIONAL_HEADER.DATA_DIRECTORY = [entry]
                mock_pefile.PE.return_value = mock_pe
                assert obj.is_dotnet() is False

    def test_is_dotnet_pe_with_com_descriptor(self):
        """PE with COM descriptor -> True."""
        obj = self._make_extractor(filetype="pebin")
        with patch("redb.extractors.decompiler.DecompileBinja.magic") as mock_magic:
            mock_magic.from_buffer.return_value = "PE32 executable"
            with patch("redb.extractors.decompiler.DecompileBinja.pefile") as mock_pefile:
                mock_pe = MagicMock()
                entry = MagicMock()
                entry.name = "IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR"
                entry.Size = 72
                mock_pe.OPTIONAL_HEADER.DATA_DIRECTORY = [entry]
                mock_pefile.PE.return_value = mock_pe
                assert obj.is_dotnet() is True


class TestDecompileBinjaGolang:
    def _make_extractor(self, filetype=None):
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.filetype = filetype
            obj.filepath = "/tmp/fake.bin"
            obj.hash = MagicMock()
            obj.hash.sha256 = "a" * 64
            return obj

    def test_is_golang_non_elf_pe(self):
        obj = self._make_extractor(filetype=None)
        assert obj.is_golang() is False

    def test_is_golang_elf_with_go_sections(self):
        """ELF with .gopclntab section -> True."""
        obj = self._make_extractor(filetype="elf")
        with patch("builtins.open", MagicMock()):
            with patch("redb.extractors.decompiler.DecompileBinja.ELFFile") as mock_elf_cls:
                mock_elf = MagicMock()
                sec1 = MagicMock()
                sec1.name = ".text"
                sec1.__getitem__ = lambda self, k: 0x2 if k == "sh_flags" else 0
                sec1.data.return_value = b""
                sec2 = MagicMock()
                sec2.name = ".gopclntab"
                sec2.__getitem__ = lambda self, k: 0x2 if k == "sh_flags" else 0
                sec2.data.return_value = b""
                mock_elf.iter_sections.return_value = [sec1, sec2]
                mock_elf_cls.return_value = mock_elf
                assert obj.is_golang() is True

    def test_is_golang_elf_without_go(self):
        obj = self._make_extractor(filetype="elf")
        with patch("builtins.open", MagicMock()):
            with patch("redb.extractors.decompiler.DecompileBinja.ELFFile") as mock_elf_cls:
                mock_elf = MagicMock()
                sec = MagicMock()
                sec.name = ".text"
                sec.__getitem__ = lambda self, k: 0x2 if k == "sh_flags" else 0
                sec.data.return_value = b"no go signatures here"
                mock_elf.iter_sections.return_value = [sec]
                mock_elf_cls.return_value = mock_elf
                assert obj.is_golang() is False


class TestDecompileBinjaTag:
    def test_tag_returns_decompiled(self):
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            assert obj.tag() == Tag.DECOMPILED.value


class TestDecompileBinjaContextManager:
    def test_context_manager_protocol(self):
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.binja_decompiler = None
            obj.goresym_output_path = None
            assert obj.__enter__() is obj
            # __exit__ calls cleanup_run
            obj.__exit__(None, None, None)

    def test_cleanup_run_clears_state(self):
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.binja_decompiler = MagicMock()
            obj.goresym_output_path = None
            obj.cleanup_run()
            assert obj.binja_decompiler is None


class TestDecompileBinjaExtract:
    def test_extract_timeout_handling(self):
        """Thread timeout -> returns None and calls cleanup."""
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.analysis_results = None
            obj.binja_decompiler = None
            obj.goresym_output_path = None
            obj.DECOMPILE_EXTRACTOR_TIMEOUT = 0  # Immediate timeout
            obj.sha256 = "a" * 64
            obj.sha1 = "b" * 40
            obj.md5 = "c" * 32
            # Mock analyze_binary to block
            import threading
            obj.analyze_binary = lambda: None
            result = obj.extract()
            assert result is None

    def test_analyze_binary_skips_dotnet(self):
        """Dotnet binary -> analyze_binary returns None."""
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.filetype = "pebin"
            obj.filepath = "/fake"
            obj.hash = MagicMock()
            obj.hash.sha256 = "a" * 64
            obj.binja_decompiler = None
            obj.goresym_output_path = None
            obj.goresym_data = None
            obj.BINJA_TIMEOUT = 60
            obj.exporters = []
            obj.index_prefix = None
            with patch.object(obj, "is_dotnet", return_value=True):
                result = obj.analyze_binary()
                assert result is None


# ============================================================================
# 7b. prepare_export_data (ClickHouse schema)
# ============================================================================


class TestPrepareExportData:
    def _make_extractor_with_results(self, goresym_data=None):
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.goresym_data = goresym_data
            obj.analysis_results = {
                "sha256": "a" * 64,
                "sha1": "b" * 40,
                "md5": "c" * 32,
                "decompiled": [
                    {
                        "decompiled_function_hash": "h" * 64,
                        "decompiled_function": "void test() {}",
                        "decompiled_function_name": "test",
                        "decompiled_function_prototype": "void test()",
                        "decompiled_function_address": 0x1000,
                        "function_type": "USER",
                        "functions_caller": [],
                        "functions_call": [],
                        "disassembled_function_hash": "d" * 64,
                        "flattened_score": 0.0,
                        "mba_score": 0.0,
                    }
                ],
                "disassembled": [
                    {
                        "disassembled_function_hash": "d" * 64,
                        "disassembled_function": "0x1000: push rbp",
                        "disassembled_function_no_addresses": "push rbp",
                        "disassembled_function_name": "test",
                        "disassembled_function_address": 0x1000,
                        "function_type": "USER",
                        "instructions_count": 5,
                        "instructions_types": ["DATA_MOVEMENT"],
                        "control_flow_count": 1,
                        "memory_access_pattern": ["MEM_STACK"],
                        "register_usage": ["GPR"],
                        "data_references_count": 0,
                        "max_block_size": 5,
                        "num_calls": 1,
                        "stack_size": -8,
                        "decompiled_function_hash": "h" * 64,
                        "tlsh_disassembly": None,
                        "tlsh_llil": None,
                        "minhash": [1, 2, 3],
                        "cyclomatic_complexity": 2,
                    }
                ],
                "cfg": [
                    {
                        "function_address": 0x1000,
                        "disassembled_function_hash": "d" * 64,
                        "cfg_topology_hash": b'\x00' * 16,
                        "block_count": 3,
                        "edge_count": 4,
                        "llil_total_operations": 20,
                        "call_count": 1,
                        "cyclomatic_complexity": 3,
                        "loop_count": 1,
                        "max_depth": 2,
                        "max_fan_out": 2,
                        "md_index_topdown": 12345,
                        "md_index_bottomup": 67890,
                        "prime_product_llil": 99999,
                        "cfg_feature_tlsh": None,
                        "wl_minhash": [0] * 128,
                        "bb_features": [[5, 1, 0, 2, 0, 1, 1, 2]] * 3,
                        "cfg_adjacency": [(0 << 16) | 1, (0 << 16) | 2, (1 << 16) | 2],
                    }
                ],
                "llil": [
                    {
                        "sha256_llil": "l" * 64,
                        "function_type": "USER",
                        "instructions_types_llil": [],
                        "control_flow_count_llil": 0,
                        "memory_access_pattern_llil": [],
                        "register_usage": {},
                        "total_reg_reads": 0,
                        "total_reg_written": 0,
                        "data_references_count": 0,
                        "max_block_size": 5,
                        "num_calls": 0,
                        "stack_size": -8,
                        "body_llil_vector": [],
                        "function_address": 0x1000,
                        "disassembled_function_hash": "d" * 64,
                        "tlsh_disassembly": None,
                        "tlsh_llil": None,
                    }
                ],
                "errors": [],
                "strings": [
                    {
                        "string": "Hello",
                        "string_raw": "Hello",
                        "string_encoding": "Utf8String",
                        "string_offset": 0,
                        "string_length": 5,
                        "string_raw_length": 5,
                        "string_entropy": 2.32,
                    }
                ],
            }
            return obj

    def test_prepare_export_returns_none_no_results(self):
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.analysis_results = None
            result = obj.prepare_export_data("ClickHouseExporter")
            assert result is None

    def test_prepare_export_multi_table_flag(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        assert result["multi_table"] is True

    def test_prepare_export_all_tables_present(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        expected_tables = [
            "decompiled_content",
            "decompiled_refs",
            "disassembled_content",
            "disassembled_refs",
            "llil_content",
            "llil_refs",
            "cfg_functions",
            "function_similarity_metrics",
            "strings_raw",
        ]
        for table in expected_tables:
            assert table in result, f"Missing table: {table}"

    def test_prepare_export_decompiled_content_schema(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        table = result["decompiled_content"]
        assert len(table["column_names"]) == len(table["column_type_names"])
        assert "decompiled_function_hash" in table["column_names"]

    def test_prepare_export_decompiled_refs_schema(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        table = result["decompiled_refs"]
        assert len(table["column_names"]) == len(table["column_type_names"])
        assert "sha256" in table["column_names"]

    def test_prepare_export_disassembled_content_schema(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        table = result["disassembled_content"]
        assert len(table["column_names"]) == len(table["column_type_names"])

    def test_prepare_export_disassembled_refs_schema(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        table = result["disassembled_refs"]
        assert len(table["column_names"]) == len(table["column_type_names"])

    def test_prepare_export_llil_content_schema(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        table = result["llil_content"]
        assert len(table["column_names"]) == len(table["column_type_names"])

    def test_prepare_export_llil_refs_schema(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        table = result["llil_refs"]
        assert len(table["column_names"]) == len(table["column_type_names"])

    def test_prepare_export_cfg_functions_schema(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        table = result["cfg_functions"]
        assert len(table["column_names"]) == len(table["column_type_names"])
        assert table["table"] == "code_binja_cfg_functions"
        assert "cfg_topology_hash" in table["column_names"]
        assert "wl_minhash" in table["column_names"]
        assert "bb_features" in table["column_names"]

    def test_prepare_export_function_similarity_schema(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        table = result["function_similarity_metrics"]
        assert len(table["column_names"]) == len(table["column_type_names"])

    def test_prepare_export_strings_raw_schema(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        table = result["strings_raw"]
        assert len(table["column_names"]) == len(table["column_type_names"])

    def test_prepare_export_errors_schema(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        table = result["function_analysis_errors"]
        assert len(table["column_names"]) == len(table["column_type_names"])

    def test_prepare_export_golang_metadata_present(self):
        obj = self._make_extractor_with_results(goresym_data={"UserFunctions": []})
        result = obj.prepare_export_data("ClickHouseExporter")
        assert "golang_metadata" in result

    def test_prepare_export_golang_metadata_absent(self):
        obj = self._make_extractor_with_results(goresym_data=None)
        result = obj.prepare_export_data("ClickHouseExporter")
        assert "golang_metadata" not in result


class TestPrepareHelperFunctions:
    """Test the helper functions inside prepare_export_data."""

    def test_prepare_array_field_none(self):
        # The prepare_array_field function converts None -> []
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.goresym_data = None
            obj.analysis_results = {
                "sha256": "a" * 64, "sha1": "b" * 40, "md5": "c" * 32,
                "decompiled": [], "disassembled": [], "cfg": [], "llil": [],
                "errors": [], "strings": [],
            }
            result = obj.prepare_export_data("ClickHouseExporter")
            # Verify the export succeeds (function works even with empty lists)
            assert result is not None

    def test_prepare_array_field_list(self):
        """List values pass through."""
        # Verified through the export data containing arrays
        pass

    def test_prepare_register_usage_map(self):
        """Dict converted to {reg: (reads, writes)} tuples."""
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.goresym_data = None
            obj.analysis_results = {
                "sha256": "a" * 64, "sha1": "b" * 40, "md5": "c" * 32,
                "decompiled": [], "disassembled": [],
                "cfg": [],
                "llil": [{
                    "sha256_llil": "l" * 64, "function_type": "USER",
                    "instructions_types_llil": [], "control_flow_count_llil": 0,
                    "memory_access_pattern_llil": [],
                    "register_usage": {"rbx": {"reads": 3, "writes": 1}},
                    "total_reg_reads": 3, "total_reg_written": 1,
                    "data_references_count": 0, "max_block_size": 5,
                    "num_calls": 0, "stack_size": 0,
                    "body_llil_vector": [],
                    "function_address": 0x1000,
                    "disassembled_function_hash": "d" * 64,
                    "tlsh_disassembly": None,
                    "tlsh_llil": None,
                }],
                "errors": [], "strings": [],
            }
            result = obj.prepare_export_data("ClickHouseExporter")
            # The LLIL content data should have register_usage_llil as a map
            llil_data = result["llil_content"]["data"]
            assert len(llil_data) == 1
            # Column index 5 is register_usage_llil
            reg_map = llil_data[0][5]
            assert reg_map == {"rbx": (3, 1)}

    def test_prepare_register_usage_map_empty(self):
        """Empty/None register usage -> {}."""
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.goresym_data = None
            obj.analysis_results = {
                "sha256": "a" * 64, "sha1": "b" * 40, "md5": "c" * 32,
                "decompiled": [], "disassembled": [],
                "cfg": [],
                "llil": [{
                    "sha256_llil": "l" * 64, "function_type": "USER",
                    "instructions_types_llil": [], "control_flow_count_llil": 0,
                    "memory_access_pattern_llil": [],
                    "register_usage": None,
                    "total_reg_reads": 0, "total_reg_written": 0,
                    "data_references_count": 0, "max_block_size": 0,
                    "num_calls": 0, "stack_size": 0,
                    "body_llil_vector": [],
                    "function_address": 0x1000,
                    "disassembled_function_hash": "d" * 64,
                    "tlsh_disassembly": None,
                    "tlsh_llil": None,
                }],
                "errors": [], "strings": [],
            }
            result = obj.prepare_export_data("ClickHouseExporter")
            llil_data = result["llil_content"]["data"]
            reg_map = llil_data[0][5]
            assert reg_map == {}